semantic-ui-react#Menu TypeScript Examples
The following examples show how to use
semantic-ui-react#Menu.
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 |
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: index.tsx From chartsy with GNU General Public License v3.0 | 6 votes |
Home: React.FC = () => {
const [showDrawer, setShowDrawer] = useState(false);
const [searchType, setSearchType] = useState(SearchType.Music);
const collageRef = createRef<HTMLDivElement>();
const MyChart = forwardRef<HTMLDivElement>((_, ref) => <Chart searchType={searchType} collageRef={ref} />);
const MyNav = forwardRef<HTMLDivElement>((_, ref) => <Nav collageRef={ref} setShowDrawer={setShowDrawer} />);
return (
<TitlesProvider>
<ConfigProvider>
<ImageGridProvider>
<MyNav ref={collageRef} />
<Sidebar.Pushable>
<Sidebar as={Menu} animation="push" onHide={() => setShowDrawer(false)} vertical visible={showDrawer}>
<ConfigMenu />
</Sidebar>
<Sidebar.Pusher>
<Search searchType={searchType} setSearchType={setSearchType} />
<div className="home" data-test="homeComponent">
<MyChart ref={collageRef} />
</div>
</Sidebar.Pusher>
</Sidebar.Pushable>
</ImageGridProvider>
</ConfigProvider>
</TitlesProvider>
);
}
Example #3
Source File: topmenu.tsx From website with MIT License | 6 votes |
useRightItems = ({ onMessageClicked }) => {
return (<>
<Menu.Item onClick={() => (window as any).swapThemeCss()}>
<Icon name='adjust' />
</Menu.Item>
<Menu.Item>
<Popup position='bottom center' flowing hoverable trigger={<Icon name='dollar' />}>
<p>We have enough reserve funds for now!</p>
<p>
Monthly cost <b>$15</b>, reserve fund <b>$205</b>
</p>
<p>
You can join our <a href='https://www.patreon.com/Datacore'>Patreon</a> for future funding rounds.
</p>
</Popup>
</Menu.Item>
<Menu.Item>
<Button size='tiny' color='green' onClick={onMessageClicked} content={'Developers needed!'} />
</Menu.Item>
<Menu.Item onClick={() => window.open('https://github.com/stt-datacore/website', '_blank')}>
<Icon name='github' />
</Menu.Item>
</>);
}
Example #4
Source File: topmenu.tsx From website with MIT License | 6 votes |
NavBarDesktop = ({ children, leftItems, narrowLayout, rightItems }) => (
<React.Fragment>
<Menu fixed='top' inverted>
{leftItems}
<Menu.Menu position='right'>{rightItems}</Menu.Menu>
</Menu>
<MainContent narrowLayout={narrowLayout}>{children}</MainContent>
</React.Fragment>
)
Example #5
Source File: topmenu.tsx From website with MIT License | 6 votes |
NavBarMobile = ({ children, leftItems, rightItems }) => {
const [visible, setVisible] = useState(false);
return (
<Sidebar.Pushable>
<Sidebar as={Menu} animation='overlay' inverted vertical onHide={() => setVisible(false)} visible={visible}>
{leftItems}
</Sidebar>
<Sidebar.Pusher dimmed={visible} style={{ minHeight: '100vh', overflowX: 'scroll' }}>
<Menu fixed='top' inverted>
<Menu.Item onClick={() => setVisible(!visible)}>
<Icon name='sidebar' />
</Menu.Item>
<Menu.Menu position='right'>{rightItems}</Menu.Menu>
</Menu>
<MainContent narrowLayout={false}>{children}</MainContent>
</Sidebar.Pusher>
</Sidebar.Pushable>
);
}
Example #6
Source File: SearchResult.tsx From watchparty with MIT License | 6 votes |
YouTubeSearchResult = (
props: SearchResult & { setMedia: Function; playlistAdd: Function }
) => {
const result = props;
const setMedia = props.setMedia;
return (
<Menu.Item
onClick={(e) => {
setMedia(e, { value: result.url });
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<img style={{ height: '50px' }} src={result.img} alt={result.name} />
<Icon color="red" size="large" name="youtube" />
<div>{decodeEntities(result.name)}</div>
<div style={{ marginLeft: 'auto' }}>
<Button
onClick={(e) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
props.playlistAdd(e, { value: result.url });
}}
>
Add To Playlist
</Button>
</div>
</div>
</Menu.Item>
);
}
Example #7
Source File: SearchResult.tsx From watchparty with MIT License | 6 votes |
MediaPathSearchResult = (
props: SearchResult & { setMedia: Function }
) => {
const result = props;
const setMedia = props.setMedia;
return (
<Menu.Item
onClick={(e) => {
setMedia(e, { value: result.url });
}}
>
<div style={{ display: 'flex' }}>
<Icon name="file" />
{result.name}
</div>
</Menu.Item>
);
}
Example #8
Source File: LobbyMeetingRoom.tsx From FLECT_Amazon_Chime_Meeting with Apache License 2.0 | 5 votes |
render() {
this.id2ref = {}
const gs = this.props as GlobalState
const props = this.props as any
const appState = props.appState as AppState
if(gs.status !== AppStatus.IN_MEETING){
return(<div />)
}
// for(let key in appState.videoTileStates){
// const attendeeId = appState.videoTileStates[key].boundAttendeeId
// const tileId = appState.videoTileStates[key].tileId
// const tmpRef = React.createRef<MainOverlayVideoElement>()
// this.id2ref[tileId!] = tmpRef
// const cell = (
// <TileScreenTile {...props} thisAttendeeId={attendeeId}/>
// )
// this.cells.push(cell)
// }
const mainScreens:JSX.Element[]=[]
if(this.state.showMainScreen===true){
for(let key in appState.joinedMeetings){
const focusAttendeeId = appState.joinedMeetings[key].focusAttendeeId
const meetingId = key
mainScreens.push(
<MainScreen {...props} thisAttendeeId={focusAttendeeId} thisMeetingId={meetingId} />
)
}
}
const tiles:JSX.Element[]=[]
if(this.state.showTileScreen === true){
for(let meetingId in appState.joinedMeetings){
const tileStates = appState.joinedMeetings[meetingId].videoTileStates
for(let tileId in tileStates){
const attendeeId = tileStates[tileId].boundAttendeeId
const tmpRef = React.createRef<MainOverlayVideoElement>()
this.id2ref[tileId] = tmpRef
const cell = (
<TileScreenTile {...props} thisAttendeeId={attendeeId} thisMeetingId={meetingId} />
)
tiles.push(cell)
}
}
}
return (
<div>
<Menu stackable secondary>
{/* <Menu.Item as="h2"
name={gs.joinInfo?.MeetingName}
>
</Menu.Item> */}
<Menu.Menu position='right'>
<Menu.Item color="teal" onClick={(e)=>{this.toggleShowMainScreen()}} active={this.state.showMainScreen}>
<Icon name="square full" />
</Menu.Item>
<Menu.Item color="teal" onClick={(e)=>{this.toggleShowTileScreen()}} active={this.state.showTileScreen}>
<Icon name="grid layout"/>
</Menu.Item>
</Menu.Menu>
</Menu>
<Grid>
<Grid.Row>
<Grid.Column>
{mainScreens}
</Grid.Column>
</Grid.Row>
<Grid.Row >
{tiles}
</Grid.Row>
</Grid>
</div>
)
}
Example #9
Source File: crewchallenge.tsx From website with MIT License | 5 votes |
CrewChallengeLayout = () => {
const [activeItem, setActiveItem] = React.useState('daily');
const menuItems = [
{ name: 'daily', title: 'Daily Game' },
{ name: 'practice', title: 'Practice Game' },
{ name: 'instructions', title: <span><Icon name='question circle outline' /> How to Play</span> }
];
return (
<Layout title={PAGE_TITLE}>
<Header as='h2'>{PAGE_TITLE}</Header>
<Menu>
{menuItems.map(item => (
<Menu.Item key={item.name} name={item.name} active={activeItem === item.name} onClick={() => setActiveItem(item.name)}>
{item.title}
</Menu.Item>
))}
</Menu>
{activeItem === 'daily' && <DailyGame />}
{activeItem === 'practice' && <PracticeGame />}
{activeItem === 'instructions' && renderInstructions()}
</Layout>
);
function renderInstructions(): JSX.Element {
const adjacentStyle = { backgroundColor: 'yellow', color: 'black', padding: '3px .5em' };
return (
<React.Fragment>
<p>How well do you know the characters from Star Trek Timelines? We pick one mystery crew member every day. Guess who it is, using your knowledge of <b>Variants</b>, <b>Series</b>, <b>Rarity</b>, <b>Skills</b>, and <b>Traits</b> to help narrow the possibilities. You have <b>{DEFAULT_GUESSES} tries</b> to guess the mystery crew.</p>
<p>Only crew that are currently <b>available in the time portal</b> will be used as mystery crew and valid guesses.</p>
<p>Anything <span style={{ backgroundColor: 'green', padding: '3px .5em' }}>highlighted green</span> indicates an exact match between your guess and the mystery crew on one or more of these criteria: series, rarity, or skills.</p>
<p>A <span style={adjacentStyle}>yellow crew name</span> indicates the mystery crew is a variant* of your guess.</p>
<p>A <span style={adjacentStyle}>yellow series</span> indicates the mystery crew is from a different series* in the same production era as your guess. The possible eras are:</p>
<ol>
<li>The Original Series, The Animated Series, and the first 6 films</li>
<li>The Next Generation, Deep Space Nine, Voyager, Enterprise, and the 4 TNG films</li>
<li>Discovery, Picard, and other shows from the current streaming era</li>
<li>Star Trek Timelines Originals (non-canonical crew)</li>
</ol>
<p>A <span style={adjacentStyle}>yellow rarity</span> indicates the mystery crew has a max rarity that is either 1 star higher or 1 star lower than your guess.</p>
<p>"<b>Skill Order</b>" lists the guessed crew's skills from highest to lowest base value. A <span style={adjacentStyle}>yellow skill</span> indicates the mystery crew has that skill, but not in the same position as your guess.</p>
<p>"<b>Traits in Common</b>" identify the traits* your guess and the mystery crew share in common.</p>
<p>* All information used here comes directly from the Star Trek Timelines game data. Variants, series, and traits may not always be what you expect; please see <a href='https://forum.wickedrealmgames.com/stt/discussion/18700/trait-audit-thread'>this thread</a> for known issues.</p>
</React.Fragment>
);
}
}
Example #10
Source File: topmenu.tsx From website with MIT License | 5 votes |
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 #11
Source File: Members2.tsx From Riakuto-StartingReact-ja3.1 with Apache License 2.0 | 5 votes |
Members: VFC<Props> = ({ orgCodeList, prefetch = () => undefined }) => {
const [orgCode, setOrgCode] = useState('');
const [input, setInput] = useState('');
const [isPending, startTransition] = useTransition();
const { reset } = useQueryErrorResetBoundary();
const menuItems = orgCodeList.map((code) => ({
key: code,
name: capitalize(code),
onClick: () => {
setInput('');
if (orgCode) {
startTransition(() => setOrgCode(code));
} else {
setOrgCode(code);
}
},
onMouseOver: () => prefetch(code),
active: code === orgCode,
}));
const handleSubmit = (event: FormEvent<HTMLFormElement>): void => {
event.preventDefault();
setOrgCode(input.toLowerCase().trim());
};
return (
<>
<header className="app-header">
<h1>組織メンバーリスト</h1>
</header>
<form className="search-form" onSubmit={handleSubmit}>
<Input
placeholder="組織コードを入力..."
type="text"
value={input}
onChange={(_, data) => setInput(data.value)}
/>
<Button type="submit" disabled={isPending} primary>
検索
</Button>
</form>
<Menu items={menuItems} text />
<Divider />
<div className={isPending ? 'loading' : ''}>
<ErrorBoundary
fallbackRender={({ resetErrorBoundary }) => (
<>
<Message warning>
{orgCode} というコードの組織は見つかりません
</Message>
<Button color="olive" onClick={() => resetErrorBoundary()}>
エラーをリセット
</Button>
</>
)}
onReset={() => reset()}
>
<SuspenseList revealOrder="forwards">
<Suspense fallback={<Spinner size="small" />}>
<OrgInfo orgCode={orgCode} />
</Suspense>
<Suspense fallback={<Spinner size="large" />}>
<MemberList orgCode={orgCode} />
</Suspense>
</SuspenseList>
</ErrorBoundary>
</div>
</>
);
}
Example #12
Source File: Members.tsx From Riakuto-StartingReact-ja3.1 with Apache License 2.0 | 5 votes |
Members: VFC<Props> = ({ orgCodeList, prefetch = () => undefined }) => {
const [orgCode, setOrgCode] = useState('');
const [input, setInput] = useState('');
const [isPending, startTransition] = useTransition();
const ebKey = useRef(0);
const menuItems = orgCodeList.map((code) => ({
key: code,
name: capitalize(code),
onClick: () => {
setInput('');
if (orgCode) {
startTransition(() => setOrgCode(code));
} else {
setOrgCode(code);
}
},
onMouseOver: () => prefetch(code),
active: code === orgCode,
}));
const handleSubmit = (event: FormEvent<HTMLFormElement>): void => {
event.preventDefault();
setOrgCode(input.toLowerCase().trim());
};
return (
<>
<header className="app-header">
<h1>組織メンバーリスト</h1>
</header>
<form className="search-form" onSubmit={handleSubmit}>
<Input
placeholder="組織コードを入力..."
type="text"
value={input}
onChange={(_, data) => setInput(data.value)}
/>
<Button type="submit" disabled={isPending} primary>
検索
</Button>
</form>
<Menu items={menuItems} text />
<Divider />
<div className={isPending ? 'loading' : ''}>
<ErrorBoundary
statusMessages={{
404: `‘${orgCode}’ というコードの組織は見つかりません`,
}}
onError={() => {
ebKey.current += 1;
}}
key={ebKey.current}
>
<SuspenseList revealOrder="forwards">
<Suspense fallback={<Spinner size="small" />}>
<OrgInfo orgCode={orgCode} />
</Suspense>
<Suspense fallback={<Spinner size="large" />}>
<MemberList orgCode={orgCode} />
</Suspense>
</SuspenseList>
</ErrorBoundary>
</div>
</>
);
}
Example #13
Source File: SearchResult.tsx From watchparty with MIT License | 5 votes |
render() {
const result = this.props;
const setMedia = this.props.setMedia;
return (
<React.Fragment>
<Menu.Item
onClick={async (e) => {
this.props.launchMultiSelect([]);
let response = await window.fetch(
this.props.streamPath +
'/data?torrent=' +
encodeURIComponent(result.magnet!)
);
let metadata = await response.json();
// console.log(metadata);
if (
metadata.files.filter(
(file: any) => file.length > 10 * 1024 * 1024
).length > 1
) {
// Multiple large files, present user selection
const multiStreamSelection = metadata.files.map(
(file: any, i: number) => ({
...file,
url:
this.props.streamPath +
'/stream?torrent=' +
encodeURIComponent(result.magnet!) +
'&fileIndex=' +
i,
})
);
multiStreamSelection.sort((a: any, b: any) =>
a.name.localeCompare(b.name)
);
this.props.launchMultiSelect(multiStreamSelection);
} else {
this.props.launchMultiSelect(undefined);
setMedia(e, {
value:
this.props.streamPath +
'/stream?torrent=' +
encodeURIComponent(result.magnet!),
});
}
}}
>
<Label
circular
empty
color={Number(result.seeders) ? 'green' : 'red'}
/>
<Icon name="film" />
{result.name +
' - ' +
result.size +
' - ' +
result.seeders +
' peers'}
</Menu.Item>
</React.Fragment>
);
}
Example #14
Source File: ComboBox.tsx From watchparty with MIT License | 5 votes |
doSearch = async (e: any) => {
e.persist();
this.setState({ inputMedia: e.target.value }, () => {
if (!this.debounced) {
this.debounced = debounce(async () => {
this.setState({ loading: true });
const query: string = this.state.inputMedia || '';
let timestamp = Number(new Date());
let results: JSX.Element[] | undefined = undefined;
if (query === '' || (query && query.startsWith('http'))) {
let items = examples;
if (!this.state.inputMedia && this.props.mediaPath) {
items = await getMediaPathResults(this.props.mediaPath, '');
}
if (query) {
items = [
{
name: query,
type: 'file',
url: query,
duration: 0,
},
];
}
results = items.map((result: SearchResult, index: number) => (
<Menu.Item
style={{ padding: '2px' }}
key={result.url}
onClick={(e: any) =>
this.setMediaAndClose(e, { value: result.url })
}
>
<ChatVideoCard
video={result}
index={index}
onPlaylistAdd={this.props.playlistAdd}
/>
</Menu.Item>
));
} else {
const data = await getYouTubeResults(query);
results = data.map((result, index) => (
<Menu.Item
key={result.url}
onClick={(e: any) =>
this.setMediaAndClose(e, { value: result.url })
}
>
<ChatVideoCard
video={result}
index={index}
onPlaylistAdd={this.props.playlistAdd}
isYoutube
/>
</Menu.Item>
));
}
if (timestamp > this.state.lastResultTimestamp) {
this.setState({
loading: false,
results,
lastResultTimestamp: timestamp,
});
}
}, 500);
}
this.debounced();
});
};
Example #15
Source File: index.tsx From frontegg-react with MIT License | 5 votes |
Tab = Menu.Item
Example #16
Source File: profile_crew2.tsx From website with MIT License | 4 votes |
render() {
const { includeFrozen, excludeFF, onlyEvent, activeItem, searchFilter } = this.state;
let { data, itemsReady } = this.state;
const { isMobile } = this.props;
if (!includeFrozen) {
data = data.filter(crew => crew.immortal === 0);
}
if (excludeFF) {
data = data.filter(crew => crew.rarity < crew.max_rarity);
}
if (searchFilter) {
data = data.filter(
crew =>
crew.name.toLowerCase().indexOf(searchFilter) !== -1 ||
crew.traits_named.some(t => t.toLowerCase().indexOf(searchFilter) !== -1) ||
crew.traits_hidden.some(t => t.toLowerCase().indexOf(searchFilter) !== -1)
);
}
const zoomFactor = isMobile ? 0.65 : 0.85;
let opts = [];
if (activeItem === '' || activeItem === this.state.defaultColumn) {
opts = ['Default Sort', 'Crew Level', 'Crew Rarity', 'Alphabetical'];
} else {
opts = ['Base Skill', 'Proficiency Skill', 'Combined Skill'];
}
let settings = ['Include Frozen', 'Exclude FF'];
let eventCrew = bonusCrewForCurrentEvent(this.props.playerData.player.character);
if (eventCrew) {
console.log(eventCrew);
settings.push(`Only event bonus (${eventCrew.eventName})`);
if (onlyEvent) {
data = data.filter(crew => eventCrew.eventCrew[crew.symbol]);
}
}
return (
<div>
<Menu attached={isMobile ? false : 'top'} fixed={isMobile ? 'top' : undefined}>
<Menu.Item
name="command_skill"
active={activeItem === 'command_skill'}
onClick={(e, { name }) => this._handleSortNew({activeItem: name})}
>
<img src={`${process.env.GATSBY_ASSETS_URL}atlas/icon_command_skill.png`} />
</Menu.Item>
<Menu.Item
name="diplomacy_skill"
active={activeItem === 'diplomacy_skill'}
onClick={(e, { name }) => this._handleSortNew({activeItem: name})}
>
<img src={`${process.env.GATSBY_ASSETS_URL}atlas/icon_diplomacy_skill.png`} />
</Menu.Item>
<Menu.Item
name="engineering_skill"
active={activeItem === 'engineering_skill'}
onClick={(e, { name }) => this._handleSortNew({activeItem: name})}
>
<img src={`${process.env.GATSBY_ASSETS_URL}atlas/icon_engineering_skill.png`} />
</Menu.Item>
<Menu.Item
name="security_skill"
active={activeItem === 'security_skill'}
onClick={(e, { name }) => this._handleSortNew({activeItem: name})}
>
<img src={`${process.env.GATSBY_ASSETS_URL}atlas/icon_security_skill.png`} />
</Menu.Item>
<Menu.Item
name="medicine_skill"
active={activeItem === 'medicine_skill'}
onClick={(e, { name }) => this._handleSortNew({activeItem: name})}
>
<img src={`${process.env.GATSBY_ASSETS_URL}atlas/icon_medicine_skill.png`} />
</Menu.Item>
<Menu.Item
name="science_skill"
active={activeItem === 'science_skill'}
onClick={(e, { name }) => this._handleSortNew({activeItem: name})}
>
<img src={`${process.env.GATSBY_ASSETS_URL}atlas/icon_science_skill.png`} />
</Menu.Item>
<DropdownOpts
opts={opts}
settings={settings}
onChange={text => this._onChange(text)}
onSettingChange={(text, val) => this._onSettingChange(text, val)}
/>
<Menu.Menu position="right">
<Menu.Item>
<Input
icon="search"
placeholder="Search..."
value={this.state.searchFilter}
onChange={(e, { value }) => this._onChangeFilter(value)}
/>
</Menu.Item>
</Menu.Menu>
</Menu>
<Segment
attached={isMobile ? false : 'bottom'}
style={isMobile ? { paddingTop: '6em', paddingBottom: '2em' } : {}}
>
<div
style={{
display: 'grid',
gridTemplateColumns: `repeat(auto-fit, minmax(${(zoomFactor * 22).toFixed(2)}em, 1fr))`,
gap: '1em'
}}
>
{data.map((crew, idx) => (
<VaultCrew key={idx} crew={crew} size={zoomFactor} itemsReady={itemsReady} />
))}
</div>
</Segment>
</div>
);
}
Example #17
Source File: ComboBox.tsx From watchparty with MIT License | 4 votes |
render() {
const { currentMedia, getMediaDisplayName } = this.props;
const { results } = this.state;
return (
<div style={{ position: 'relative' }}>
<div style={{ display: 'flex', gap: '4px' }}>
<Input
style={{ flexGrow: 1 }}
inverted
fluid
focus
disabled={this.props.disabled}
onChange={this.doSearch}
onFocus={async (e: any) => {
e.persist();
this.setState(
{
inputMedia: getMediaDisplayName(currentMedia),
},
() => {
if (
!this.state.inputMedia ||
(this.state.inputMedia &&
this.state.inputMedia.startsWith('http'))
) {
this.doSearch(e);
}
}
);
setTimeout(() => e.target.select(), 100);
}}
onBlur={() => {
setTimeout(
() =>
this.setState({ inputMedia: undefined, results: undefined }),
100
);
}}
onKeyPress={(e: any) => {
if (e.key === 'Enter') {
this.setMediaAndClose(e, {
value: this.state.inputMedia,
});
}
}}
icon={
<Icon
onClick={(e: any) =>
this.setMediaAndClose(e, {
value: this.state.inputMedia,
})
}
name="arrow right"
link
circular
//bordered
/>
}
loading={this.state.loading}
label={'Now Watching:'}
placeholder="Enter video file URL, YouTube link, or YouTube search term"
value={
this.state.inputMedia !== undefined
? this.state.inputMedia
: getMediaDisplayName(currentMedia)
}
/>
<Dropdown
icon="list"
labeled
className="icon"
button
text={`Playlist (${this.props.playlist.length})`}
scrolling
>
<Dropdown.Menu direction="left">
{this.props.playlist.length === 0 && (
<Dropdown.Item disabled>
There are no items in the playlist.
</Dropdown.Item>
)}
{this.props.playlist.map((item: PlaylistVideo, index: number) => {
return (
<Dropdown.Item>
<div style={{ maxWidth: '500px' }}>
<ChatVideoCard
video={item}
index={index}
controls
onPlay={(index) => {
this.props.setMedia(null, {
value: this.props.playlist[index]?.url,
});
this.props.playlistDelete(index);
}}
onPlayNext={(index) => {
this.props.playlistMove(index, 0);
}}
onRemove={(index) => {
this.props.playlistDelete(index);
}}
disabled={this.props.disabled}
isYoutube={Boolean(item.img)}
/>
</div>
</Dropdown.Item>
);
})}
</Dropdown.Menu>
</Dropdown>
</div>
{Boolean(results) && this.state.inputMedia !== undefined && (
<Menu
fluid
vertical
style={{
position: 'absolute',
top: '22px',
maxHeight: '250px',
overflow: 'scroll',
zIndex: 1001,
}}
>
{results}
</Menu>
)}
</div>
);
}
Example #18
Source File: App.tsx From watchparty with MIT License | 4 votes |
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>
);
}
Example #19
Source File: playertools.tsx From website with MIT License | 4 votes |
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 #20
Source File: profile.tsx From website with MIT License | 4 votes |
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>
);
}