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 |
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 |
////////////////////////////////
/// 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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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>
);
}