semantic-ui-react#Input TypeScript Examples
The following examples show how to use
semantic-ui-react#Input.
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: PasswordModal.tsx From watchparty with MIT License | 6 votes |
PasswordModal = ({
savedPasswords,
roomId,
}: {
savedPasswords: StringDict;
roomId: string;
}) => {
const setPassword = useCallback(() => {
window.localStorage.setItem(
'watchparty-passwords',
JSON.stringify({
...savedPasswords,
[roomId]: (document.getElementById('roomPassword') as HTMLInputElement)
?.value,
})
);
window.location.reload();
}, [savedPasswords, roomId]);
return (
<Modal inverted basic open>
<Header as="h1" style={{ textAlign: 'center' }}>
This room requires a password.
</Header>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Input
id="roomPassword"
type="password"
size="large"
onKeyPress={(e: any) => e.key === 'Enter' && setPassword()}
icon={
<Icon onClick={setPassword} name="key" inverted circular link />
}
/>
</div>
</Modal>
);
}
Example #2
Source File: ExampleList.tsx From plugin-vscode with Apache License 2.0 | 5 votes |
public render() {
return (
<Grid className="welcome-page" divided>
<Grid.Row className="welcome-navbar" columns={1}>
<Grid.Column>
<Header as="h3" dividing>
Ballerina Examples
</Header>
{this.state && this.state.noSearchReults ?
(<>No search results found!</>) : null
}
{this.state && this.state.samples && this.state.samples.length > 0 ?
(
<Form>
<Form.Field inline>
<Input
ref={(ref) => {
this.searchInput = ref as Input;
}}
loading={!this.state || !this.state.samples}
placeholder="Search"
onChange={(event: React.SyntheticEvent<HTMLInputElement>) => {
this.setState({
searchQuery: event.currentTarget.value,
});
this.onSearchQueryEdit();
}}
className="search-control"
/>
</Form.Field>
</Form>
) : (
<>No Samples found in [BALLERINA_HOME]/examples folder.</>
)
}
</Grid.Column>
</Grid.Row>
<Grid.Row className="welcome-content-wrapper">
<Grid.Column mobile={16} tablet={16} computer={16} className="rightContainer">
<Grid>
{this.state && this.state.samples &&
<Grid.Row columns={4} className="sample-wrapper">
{
this.getColumnContents().map((column, index) => {
return (
<Grid.Column key={index} mobile={16} tablet={8} computer={4}>
{column.map((columnItem) => this.renderColumnItem(columnItem))}
</Grid.Column>
);
})
}
</Grid.Row>
}
</Grid>
</Grid.Column>
</Grid.Row>
</Grid>);
}
Example #3
Source File: ExampleList.tsx From plugin-vscode with Apache License 2.0 | 5 votes |
private searchInput: Input | undefined;
Example #4
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 #5
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 #6
Source File: index.tsx From chartsy with GNU General Public License v3.0 | 4 votes |
Search: React.FC<Props> = ({ searchType, setSearchType }) => {
const { dispatch } = useImageGrid();
const [activeButton, setActiveButton] = useState("music");
const [results, setResults] = useState<Image[]>([]);
const [tmp, setVal] = useState("");
const [search, setSearch] = useState("");
useEffect(() => {
const download = async () => {
if (search === "") {
return;
}
switch (searchType) {
case SearchType.Games:
const albums = await getGame(search);
setResults(albums);
break;
case SearchType.Movies:
const movies = await getMovie(search);
setResults(movies);
break;
case SearchType.Series:
const series = await getSeries(search);
setResults(series);
break;
default:
const games = await getAlbum(search);
setResults(games);
break;
}
};
download();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [search]);
return (
<div id="search">
<Grid.Row>
<Button.Group fluid>
<Button
basic
active={activeButton === "music"}
icon="music"
onClick={(e) => {
dispatch({ type: "reset" });
setSearchType(SearchType.Music);
setActiveButton("music");
e.preventDefault();
}}
/>
<Button
basic
active={activeButton === "game"}
icon="game"
onClick={(e) => {
dispatch({ type: "reset" });
setSearchType(SearchType.Games);
setActiveButton("game");
e.preventDefault();
}}
/>
{/* <Button */}
{/* basic */}
{/* active={activeButton === "film"} */}
{/* icon="film" */}
{/* onClick={(e) => { */}
{/* setSearchType(SearchType.Movies); */}
{/* setActiveButton("film"); */}
{/* e.preventDefault(); */}
{/* }} */}
{/* /> */}
{/* <Button */}
{/* basic */}
{/* active={activeButton === "tv"} */}
{/* icon="tv" */}
{/* onClick={(e) => { */}
{/* setSearchType(SearchType.Series); */}
{/* setActiveButton("tv"); */}
{/* e.preventDefault(); */}
{/* }} */}
{/* /> */}
</Button.Group>
<form
className="search-bar"
onSubmit={(e) => {
setSearch(tmp);
e.preventDefault();
}}
>
<Input
fluid
placeholder={`Search...${searchType === SearchType.Games ? " powered by RAWG.io" : ""}`}
value={tmp}
onChange={(e) => {
setVal(e.target.value);
e.preventDefault();
}}
/>
</form>
</Grid.Row>
<Grid.Row>
<Grid className="results" centered padded>
{results.map((img) => (
<Grid.Row>
<ImageCard onGrid={false} key={img.url} img={img} showTitle={true} />
</Grid.Row>
))}
</Grid>
</Grid.Row>
</div>
);
}
Example #7
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 #8
Source File: Chat.tsx From watchparty with MIT License | 4 votes |
render() {
return (
<div
className={this.props.className}
style={{
display: this.props.hide ? 'none' : 'flex',
flexDirection: 'column',
flexGrow: 1,
minHeight: 0,
marginTop: 0,
marginBottom: 0,
padding: '8px',
}}
>
<div
className="chatContainer"
ref={this.messagesRef}
style={{ position: 'relative', paddingTop: 13 }}
>
<Comment.Group>
{this.props.chat.map((msg) => (
<ChatMessage
key={msg.timestamp + msg.id}
className={
msg.id === this.state.reactionMenu.selectedMsgId &&
msg.timestamp === this.state.reactionMenu.selectedMsgTimestamp
? classes.selected
: ''
}
message={msg}
pictureMap={this.props.pictureMap}
nameMap={this.props.nameMap}
formatMessage={this.formatMessage}
owner={this.props.owner}
user={this.props.user}
socket={this.props.socket}
isChatDisabled={this.props.isChatDisabled}
setReactionMenu={this.setReactionMenu}
handleReactionClick={this.handleReactionClick}
/>
))}
{/* <div ref={this.messagesEndRef} /> */}
</Comment.Group>
{!this.state.isNearBottom && (
<Button
size="tiny"
onClick={this.scrollToBottom}
style={{
position: 'sticky',
bottom: 0,
display: 'block',
margin: '0 auto',
}}
>
Jump to bottom
</Button>
)}
</div>
<Separator />
{this.state.isPickerOpen && (
<PickerMenu
addEmoji={this.addEmoji}
closeMenu={() => this.setState({ isPickerOpen: false })}
/>
)}
<CSSTransition
in={this.state.reactionMenu.isOpen}
timeout={300}
classNames={{
enter: classes['reactionMenu-enter'],
enterActive: classes['reactionMenu-enter-active'],
exit: classes['reactionMenu-exit'],
exitActive: classes['reactionMenu-exit-active'],
}}
unmountOnExit
>
<ReactionMenu
handleReactionClick={this.handleReactionClick}
closeMenu={() => this.setReactionMenu(false)}
yPosition={this.state.reactionMenu.yPosition}
xPosition={this.state.reactionMenu.xPosition}
/>
</CSSTransition>
<Input
inverted
fluid
onKeyPress={(e: any) => e.key === 'Enter' && this.sendChatMsg()}
onChange={this.updateChatMsg}
value={this.state.chatMsg}
error={this.chatTooLong()}
icon
disabled={this.props.isChatDisabled}
placeholder={
this.props.isChatDisabled
? 'The chat was disabled by the room owner.'
: 'Enter a message...'
}
>
<input />
<Icon
// style={{ right: '40px' }}
onClick={() => this.setState({ isPickerOpen: true })}
name={'' as any}
inverted
circular
link
disabled={this.props.isChatDisabled}
style={{ opacity: 1 }}
>
<span role="img" aria-label="Emoji">
?
</span>
</Icon>
{/* <Icon onClick={this.sendChatMsg} name="send" inverted circular link /> */}
</Input>
</div>
);
}
Example #9
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 #10
Source File: SettingsTab.tsx From watchparty with MIT License | 4 votes |
SettingsTab = ({
hide,
user,
roomLock,
setRoomLock,
socket,
isSubscriber,
owner,
vanity,
setVanity,
roomLink,
password,
setPassword,
isChatDisabled,
setIsChatDisabled,
clearChat,
roomTitle,
roomDescription,
roomTitleColor,
mediaPath,
setMediaPath,
}: SettingsTabProps) => {
const [updateTS, setUpdateTS] = useState(0);
const [permModalOpen, setPermModalOpen] = useState(false);
const [validVanity, setValidVanity] = useState(true);
const [validVanityLoading, setValidVanityLoading] = useState(false);
const [adminSettingsChanged, setAdminSettingsChanged] = useState(false);
const [roomTitleInput, setRoomTitleInput] = useState<string | undefined>(undefined);
const [roomDescriptionInput, setRoomDescriptionInput] = useState<
string | undefined
>(undefined);
const [roomTitleColorInput, setRoomTitleColorInput] = useState<
string | undefined
>('');
const setRoomState = useCallback(
async (data: any) => {
const token = await user?.getIdToken();
socket.emit('CMD:setRoomState', {
uid: user?.uid,
token,
...data,
});
},
[socket, user]
);
const setRoomOwner = useCallback(
async (data: any) => {
const token = await user?.getIdToken();
socket.emit('CMD:setRoomOwner', {
uid: user?.uid,
token,
...data,
});
},
[socket, user]
);
const checkValidVanity = useCallback(
async (input: string) => {
if (!input) {
setValidVanity(true);
return;
}
setValidVanity(false);
setValidVanityLoading(true);
const response = await axios.get(serverPath + '/resolveRoom/' + input);
const data = response.data;
setValidVanityLoading(false);
if (
data &&
data.vanity &&
data.vanity !== roomLink.split('/').slice(-1)[0]
) {
// Already exists and doesn't match current room
setValidVanity(false);
} else {
setValidVanity(true);
}
},
[setValidVanity, roomLink]
);
const disableLocking =
!Boolean(user) || Boolean(roomLock && roomLock !== user?.uid);
const disableOwning = !Boolean(user) || Boolean(owner && owner !== user?.uid);
return (
<div
style={{
display: hide ? 'none' : 'block',
color: 'white',
overflow: 'scroll',
padding: '8px',
}}
>
{permModalOpen && (
<PermanentRoomModal
closeModal={() => setPermModalOpen(false)}
></PermanentRoomModal>
)}
<div className="sectionHeader">Room Settings</div>
{!user && (
<Message color="yellow" size="tiny">
You need to be signed in to change these settings.
</Message>
)}
<SettingRow
icon={roomLock ? 'lock' : 'lock open'}
name={`Lock Room`}
description="Only the person who locked the room can control the video."
checked={Boolean(roomLock)}
disabled={disableLocking && disableOwning}
onChange={(_e, data) => setRoomLock(data.checked)}
/>
{
<SettingRow
icon={'clock'}
name={`Make Room Permanent`}
description={
'Prevent this room from expiring. This also unlocks additional room features.'
}
helpIcon={
<Icon
name="help circle"
onClick={() => setPermModalOpen(true)}
style={{ cursor: 'pointer' }}
></Icon>
}
checked={Boolean(owner)}
disabled={disableOwning}
onChange={(_e, data) => setRoomOwner({ undo: !data.checked })}
/>
}
{owner && owner === user?.uid && (
<div className="sectionHeader">Admin Settings</div>
)}
{owner && owner === user?.uid && (
<SettingRow
icon={'key'}
name={`Set Room Password`}
description="Users must know this password in order to join the room."
content={
<Input
value={password}
size="mini"
onChange={(e) => {
setAdminSettingsChanged(true);
setPassword(e.target.value);
}}
fluid
/>
}
disabled={false}
/>
)}
{owner && owner === user?.uid && (
<SettingRow
icon={'folder'}
name={`Set Room Media Source`}
description="Set a media source URL with files to replace the default examples. Supports S3 buckets and nginx file servers."
content={
<Input
value={mediaPath}
size="mini"
onChange={(e) => {
setAdminSettingsChanged(true);
setMediaPath(e.target.value);
}}
fluid
/>
}
disabled={false}
/>
)}
{owner && owner === user?.uid && (
<SettingRow
icon={'i cursor'}
name={`Disable Chat`}
description="Prevent users from sending messages in chat."
checked={Boolean(isChatDisabled)}
disabled={false}
onChange={(_e, data) => {
setAdminSettingsChanged(true);
setIsChatDisabled(Boolean(data.checked));
}}
/>
)}
{owner && owner === user?.uid && (
<SettingRow
icon={'i delete'}
name={`Clear Chat`}
description="Delete all existing chat messages"
disabled={false}
content={
<Button
color="red"
icon
labelPosition="left"
onClick={() => clearChat()}
>
<Icon name="delete" />
Delete Messages
</Button>
}
/>
)}
{owner && owner === user?.uid && (
<SettingRow
icon={'linkify'}
name={`Set Custom Room URL`}
description="Set a custom URL for this room. Inappropriate names may be revoked."
checked={Boolean(roomLock)}
disabled={!isSubscriber}
subOnly={true}
content={
<React.Fragment>
<Input
value={vanity}
disabled={!isSubscriber}
onChange={(e) => {
setAdminSettingsChanged(true);
checkValidVanity(e.target.value);
setVanity(e.target.value);
}}
label={<Label>{`${window.location.origin}/r/`}</Label>}
loading={validVanityLoading}
fluid
size="mini"
icon
action={
validVanity ? (
<Icon name="checkmark" color="green" />
) : (
<Icon name="close" color="red" />
)
}
></Input>
</React.Fragment>
}
/>
)}
{owner && owner === user?.uid && (
<SettingRow
icon={'pencil'}
name={`Set Room Title, Description & Color`}
description="Set the room title, description and title color to be displayed in the top bar."
disabled={!isSubscriber}
subOnly={true}
content={
<React.Fragment>
<div style={{ display: 'flex', marginBottom: 2 }}>
<Input
style={{ marginRight: 3, flexGrow: 1 }}
value={roomTitleInput ?? roomTitle}
disabled={!isSubscriber}
maxLength={roomTitleMaxCharLength}
onChange={(e) => {
setAdminSettingsChanged(true);
setRoomTitleInput(e.target.value);
}}
placeholder={`Title (max. ${roomTitleMaxCharLength} characters)`}
fluid
size="mini"
icon
></Input>
<Popup
content={
<React.Fragment>
<h5>Edit Title Color</h5>
<HexColorPicker
color={
roomTitleColorInput ||
roomTitleColor ||
defaultRoomTitleColor
}
onChange={(e) => {
setAdminSettingsChanged(true);
setRoomTitleColorInput(e);
}}
/>
<div
style={{
marginTop: 8,
paddingLeft: 4,
borderLeft: `24px solid ${roomTitleColorInput}`,
}}
>
{roomTitleColorInput?.toUpperCase()}
</div>
</React.Fragment>
}
on="click"
trigger={
<Button
icon
color="teal"
size="tiny"
style={{ margin: 0 }}
disabled={!isSubscriber}
>
<Icon name="paint brush" />
</Button>
}
/>
</div>
<Input
style={{ marginBottom: 2 }}
value={roomDescriptionInput ?? roomDescription}
disabled={!isSubscriber}
maxLength={roomDescriptionMaxCharLength}
onChange={(e) => {
setAdminSettingsChanged(true);
setRoomDescriptionInput(e.target.value);
}}
placeholder={`Description (max. ${roomDescriptionMaxCharLength} characters)`}
fluid
size="mini"
icon
></Input>
</React.Fragment>
}
/>
)}
<div
style={{
borderTop: '3px dashed white',
marginTop: 10,
marginBottom: 10,
}}
/>
{owner && owner === user?.uid && (
<Button
primary
disabled={!validVanity || !adminSettingsChanged}
labelPosition="left"
icon
fluid
onClick={() => {
setRoomState({
vanity: vanity,
password: password,
isChatDisabled: isChatDisabled,
roomTitle: roomTitleInput ?? roomTitle,
roomDescription: roomDescriptionInput ?? roomDescription,
roomTitleColor:
roomTitleColorInput || roomTitleColor || defaultRoomTitleColor,
mediaPath: mediaPath,
});
setAdminSettingsChanged(false);
}}
>
<Icon name="save" />
Save Admin Settings
</Button>
)}
<div className="sectionHeader">Local Settings</div>
<SettingRow
updateTS={updateTS}
icon="bell"
name="Disable chat notification sound"
description="Don't play a sound when a chat message is sent while you're on another tab"
checked={Boolean(getCurrentSettings().disableChatSound)}
disabled={false}
onChange={(_e, data) => {
updateSettings(
JSON.stringify({
...getCurrentSettings(),
disableChatSound: data.checked,
})
);
setUpdateTS(Number(new Date()));
}}
/>
</div>
);
}
Example #11
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 #12
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 #13
Source File: missionslist.tsx From website with MIT License | 4 votes |
MissionsList = (props: MissionsListProps) => {
const { groupId, shuttlers, setShuttlers, activeShuttles } = props;
const [editMission, setEditMission] = React.useState(undefined);
const [state, dispatch] = React.useReducer(reducer, {
data: shuttlers.shuttles.filter(shuttle => shuttle.groupId === groupId),
column: null,
direction: null
});
const { data, column, direction } = state;
React.useEffect(() => {
dispatch({ type: 'UPDATE_DATA', data: shuttlers.shuttles.filter(shuttle => shuttle.groupId === groupId), column, direction });
}, [shuttlers]);
const CheckDropdown = () => {
if (data.length === 0) return (<></>);
const checkOptions = [];
const threeSeaters = [], fourSeaters = [];
data.forEach(shuttle => {
if (shuttle.seats.length <= 4)
fourSeaters.push(shuttle.id);
if (shuttle.seats.length === 3)
threeSeaters.push(shuttle.id);
});
if (threeSeaters.length > 0)
checkOptions.push({ key: 'three-seaters', text: `Select only 3-seaters (${threeSeaters.length})`, ids: threeSeaters });
if (fourSeaters.length > 0)
checkOptions.push({ key: 'four-seaters', text: `Select only 3- and 4- seaters (${fourSeaters.length})`, ids: fourSeaters });
if (activeShuttles?.length > 0) {
const openIds = activeShuttles.map(adventure => adventure.symbol);
checkOptions.push({ key: `open-adventures`, text: `Select only open in-game (${openIds.length})`, ids: openIds });
}
const factions = [];
data.forEach(shuttle => {
if (shuttle.faction > 0 && !factions.includes(shuttle.faction)) factions.push(shuttle.faction);
});
if (factions.length > 1) {
factions.forEach(factionId => {
const ids = data.filter(shuttle => shuttle.faction === factionId).map(shuttle => shuttle.id);
const faction = allFactions.find(af => af.id === factionId);
checkOptions.push({ key: `faction-${factionId}`, text: `Select only ${faction.name} (${ids.length})`, ids });
});
}
return (
<Dropdown
icon='check'
floating
>
<Dropdown.Menu>
<Dropdown.Item icon='check' text={`Select all (${data.length})`} onClick={() => checkMissions([])} />
{missionsSelected > 0 && (
<Dropdown.Item icon='x' text='Unselect all' onClick={() => checkMissions([], false)} />
)}
{checkOptions.length > 0 && <Dropdown.Divider />}
{checkOptions.map(option => (
<Dropdown.Item key={option.key} text={option.text} onClick={() => checkMissions(option.ids)} />
))}
</Dropdown.Menu>
</Dropdown>
);
};
const tableConfig = [
{ title: <CheckDropdown />, align: 'center' },
{ column: 'name', title: 'Mission' },
{ column: 'faction', title: 'Faction', align: 'center' },
{ column: 'seats.length', title: 'Seats', align: 'center' },
{ column: 'skills', title: 'Skills', span: 5 },
{ title: '' }
];
const MissionEditor = (props: { shuttle: Shuttle }) => {
const [shuttle, setShuttle] = React.useState(JSON.parse(JSON.stringify(props.shuttle)));
const factionOptions = allFactions.sort((a, b) => a.name.localeCompare(b.name)).map(faction => {
return { key: faction.id, value: faction.id, text: (<span style={{ whiteSpace: 'nowrap' }}>{faction.name}</span>) };
});
const EditorSeat = (props: { seat: ShuttleSeat, seatNum: number }) => {
const { seatNum, seat } = props;
const skillOptions = [
{ key: 'CMD', text: 'CMD', value: 'command_skill' },
{ key: 'DIP', text: 'DIP', value: 'diplomacy_skill' },
{ key: 'ENG', text: 'ENG', value: 'engineering_skill' },
{ key: 'MED', text: 'MED', value: 'medicine_skill' },
{ key: 'SCI', text: 'SCI', value: 'science_skill' },
{ key: 'SEC', text: 'SEC', value: 'security_skill' }
];
return (
<Grid textAlign='center' columns={3}>
<Grid.Column>
<Dropdown
direction='right'
compact
selection
options={skillOptions}
value={seat.skillA}
onChange={(e, { value }) => updateMissionSeat(seatNum, 'skillA', value)}
/>
</Grid.Column>
<Grid.Column>
<Button circular
disabled={seat.skillB == '' ? true : false}
onClick={() => updateMissionSeat(seatNum, 'operand', seat.operand == 'AND' ? 'OR' : 'AND')}
>
{seat.skillB == '' ? '' : seat.operand}
</Button>
</Grid.Column>
<Grid.Column>
<Dropdown
compact
selection
clearable
options={skillOptions}
value={seat.skillB}
onChange={(e, { value }) => updateMissionSeat(seatNum, 'skillB', value)}
/>
</Grid.Column>
</Grid>
);
};
return (
<Modal
open={true}
onClose={() => applyEdits()}
>
<Modal.Header>Edit Mission</Modal.Header>
<Modal.Content scrolling>
{renderContent()}
</Modal.Content>
<Modal.Actions>
<Button positive onClick={() => applyEdits()}>
Close
</Button>
</Modal.Actions>
</Modal>
);
function renderContent(): void {
return (
<React.Fragment>
<Grid columns={2} divided stackable>
<Grid.Column>
<div>
<Header as='h4'>Mission name</Header>
<Input style={{ marginTop: '-1em' }}
placeholder='Mission name...'
value={shuttle.name}
onChange={(e, { value }) => updateMissionName(value)}>
<input />
<Button icon onClick={() => updateMissionName('')} >
<Icon name='delete' />
</Button>
</Input>
</div>
<div style={{ marginTop: '1em' }}>
<Header as='h4'>Faction</Header>
<Dropdown
style={{ marginTop: '-1em' }}
selection
options={factionOptions}
value={shuttle.faction}
onChange={(e, { value }) => updateFaction(value)}
/>
</div>
</Grid.Column>
<Grid.Column>
<Header as='h4'>Seats</Header>
<p style={{ marginTop: '-1em' }}>Set each seat to the skills required. Add seats as necessary.</p>
<Table collapsing unstackable compact='very' size='small'>
<Table.Body>
{shuttle.seats.map((seat, seatNum) => (
<Table.Row key={seatNum}>
<Table.Cell textAlign='right'>{seatNum+1}</Table.Cell>
<Table.Cell textAlign='center'>
<EditorSeat seatNum={seatNum} seat={seat} />
</Table.Cell>
<Table.Cell textAlign='right'>
{shuttle.seats.length > 1 && <Button compact icon='trash' color='red' onClick={() => deleteMissionSeat(seatNum)} />}
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
<Button compact icon='plus square outline' content='Add Seat' onClick={() => addMissionSeat()} />
</Grid.Column>
</Grid>
<div style={{ marginTop: '1em' }}>
<Divider />
<p>If you no longer need this mission, you can delete it here. Note: missions will be automatically deleted after the event has concluded.</p>
<p><Button icon='trash' color='red' content='Delete Mission' onClick={() => deleteMission(shuttle.id)} /></p>
</div>
</React.Fragment>
);
}
function updateMissionName(newName: string): void {
shuttle.name = newName;
setShuttle({...shuttle});
}
function updateFaction(newFaction: number): void {
shuttle.faction = newFaction;
setShuttle({...shuttle});
}
function updateMissionSeat(seatNum: number, key: string, value: string): void {
shuttle.seats[seatNum][key] = value;
setShuttle({...shuttle});
}
function addMissionSeat(): void {
shuttle.seats.push(new ShuttleSeat());
setShuttle({...shuttle});
}
function deleteMissionSeat(seatNum: number): void {
shuttle.seats.splice(seatNum, 1);
setShuttle({...shuttle});
}
function applyEdits(): void {
if (shuttle.priority === 0) shuttle.priority = missionsSelected + 1;
const shuttleNum = shuttlers.shuttles.findIndex(s => s.id === shuttle.id);
shuttlers.shuttles[shuttleNum] = shuttle;
updateShuttlers();
setEditMission(undefined);
}
};
const missionsSelected = data.filter(shuttle => shuttle.priority > 0).length;
return (
<React.Fragment>
<div>Click all the missions that you want to run, then click 'Recommend Crew' to see the best seats for your crew.</div>
<Table celled striped selectable sortable singleLine>
<Table.Header>
<Table.Row>
{tableConfig.map((cell, idx) => (
<Table.HeaderCell key={idx}
sorted={column === cell.column ? direction : null}
onClick={() => dispatch({ type: 'CHANGE_SORT', column: cell.column, reverse: cell.reverse })}
colSpan={cell.span ?? 1}
textAlign={cell.align ?? 'left'}
>
{cell.title}
</Table.HeaderCell>
))}
</Table.Row>
</Table.Header>
<Table.Body>
{data.length === 0 && (
<Table.Row>
<Table.Cell colSpan={10} textAlign='center'>
No missions available.
</Table.Cell>
</Table.Row>
)}
{data.map(shuttle => (
<Table.Row key={shuttle.id} style={{ cursor: 'pointer' }}
onClick={() => toggleMissionStatus(shuttle.id)}
onDoubleClick={() => { toggleMissionStatus(shuttle.id); props.recommendShuttlers(); }}
>
<Table.Cell textAlign='center'>
{shuttle.priority > 0 && (<Icon color='green' name='check' />)}
</Table.Cell>
<Table.Cell>
<span style={{ fontSize: '1.1em' }}><b>{shuttle.name}</b></span>
</Table.Cell>
<Table.Cell textAlign='center'>
<ShuttleFactionView factionId={shuttle.faction} size={1.5} />
</Table.Cell>
<Table.Cell textAlign='center'>{shuttle.seats.length}</Table.Cell>
{[0, 1, 2, 3, 4].map(seatNum => (
<Table.Cell key={seatNum} textAlign='center'>
{shuttle.seats.length > seatNum && (
<SeatSkillView seat={shuttle.seats[seatNum]} />
)}
</Table.Cell>
))}
<Table.Cell textAlign='right'>
{!shuttle.readonly && (
<Button icon='edit' content='Edit' onClick={(e) => { setEditMission(shuttle); e.stopPropagation(); }}/>
)}
</Table.Cell>
</Table.Row>
))}
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.HeaderCell colSpan={10} textAlign='right'>
{missionsSelected > 0 && (<Button compact icon='rocket' color='green' content='Recommend Crew' onClick={() => props.recommendShuttlers()} />)}
{missionsSelected === 0 && (<Button compact icon='rocket' content='Recommend Crew' />)}
</Table.HeaderCell>
</Table.Row>
</Table.Footer>
</Table>
{editMission && <MissionEditor shuttle={editMission} />}
<p>If the mission you want isn't listed here, click 'Create Mission' to input the mission parameters manually. Tip: open shuttle missions in-game before uploading your player data to DataCore so that this tool can import the missions automatically.</p>
<Button icon='plus square' content='Create Mission' onClick={() => createMission() } />
</React.Fragment>
);
function reducer(state, action): any {
switch (action.type) {
case 'UPDATE_DATA':
//const defaultColumn = action.data.filter(shuttle => shuttle.priority > 0).length ? 'priority' : 'name';
const updatedData = action.data.slice();
firstSort(updatedData, action.column ?? 'name', action.direction ?? 'ascending');
return {
column: action.column ?? 'name',
data: updatedData,
direction: action.direction ?? 'ascending'
};
case 'CHANGE_SORT':
if (!action.column) {
return {
column: state.column,
data: state.data,
direction: state.direction
};
}
if (state.column === action.column && action.column !== 'priority') {
return {
...state,
data: state.data.slice().reverse(),
direction: state.direction === 'ascending' ? 'descending' : 'ascending'
};
}
else {
const data = state.data.slice();
firstSort(data, action.column, action.reverse);
return {
column: action.column,
data: data,
direction: action.reverse ? 'descending' : 'ascending'
};
}
default:
throw new Error();
}
}
function firstSort(data: any[], column: string, reverse: boolean = false): any[] {
data.sort((a, b) => {
if (column === 'name') return a.name.localeCompare(b.name);
let aValue = column.split('.').reduce((prev, curr) => prev.hasOwnProperty(curr) ? prev[curr] : undefined, a);
let bValue = column.split('.').reduce((prev, curr) => prev.hasOwnProperty(curr) ? prev[curr] : undefined, b);
// Always show selected missions at the top when sorting by priority
if (column === 'priority') {
if (aValue === 0) aValue = 100;
if (bValue === 0) bValue = 100;
}
if (column === 'skills') {
aValue = a.seats.length;
bValue = b.seats.length;
}
// Tiebreaker goes to name ascending
if (aValue === bValue) return a.name.localeCompare(b.name);
if (reverse) bValue - aValue;
return aValue - bValue;
});
}
function checkMissions(shuttleIds: string[], checkState: boolean = true): void {
let priority = 0;
shuttlers.shuttles.forEach(shuttle => {
if (shuttleIds.length === 0)
shuttle.priority = checkState ? ++priority : 0;
else
shuttle.priority = checkState && shuttleIds.includes(shuttle.id) ? ++priority : 0;
});
updateShuttlers();
if (shuttleIds.length !== 0)
dispatch({ type: 'CHANGE_SORT', column: 'priority' });
}
function createMission(): void {
const shuttle = new Shuttle(groupId);
shuttle.seats.push(new ShuttleSeat());
shuttlers.shuttles.push(shuttle);
updateShuttlers();
setEditMission(shuttle);
}
function deleteMission(shuttleId: string): void {
const shuttleNum = shuttlers.shuttles.findIndex(shuttle => shuttle.id === shuttleId);
shuttlers.shuttles.splice(shuttleNum, 1);
updateShuttlers();
setEditMission(undefined);
}
function toggleMissionStatus(shuttleId: string): void {
const shuttle = shuttlers.shuttles.find(shuttle => shuttle.id === shuttleId);
shuttle.priority = shuttle.priority === 0 ? missionsSelected+1 : 0;
updateShuttlers();
}
function updateShuttlers(): void {
setShuttlers({...shuttlers});
}
}
Example #14
Source File: crewchallenge.tsx From website with MIT License | 4 votes |
CrewPicker = (props: CrewPickerProps) => {
const { rules, guesses, handleSelect } = props;
const portalCrew = React.useContext(PortalCrewContext);
const [modalIsOpen, setModalIsOpen] = React.useState(false);
const [searchFilter, setSearchFilter] = React.useState('');
const [paginationPage, setPaginationPage] = React.useState(1);
const [selectedCrew, setSelectedCrew] = React.useState(undefined);
const [showHints, setShowHints] = React.useState(true);
const guessesLeft = rules.guesses - guesses.length;
const inputRef = React.createRef();
React.useEffect(() => {
if (modalIsOpen) inputRef.current.focus();
}, [modalIsOpen]);
return (
<Modal
open={modalIsOpen}
onClose={() => setModalIsOpen(false)}
onOpen={() => setModalIsOpen(true)}
trigger={renderButton()}
size='tiny'
centered={false}
closeIcon
>
<Modal.Header>
<Input ref={inputRef}
size='mini' fluid
iconPosition='left'
placeholder='Search for crew by name'
value={searchFilter}
onChange={(e, { value }) => { setSearchFilter(value); setPaginationPage(1); setSelectedCrew(undefined); }}>
<input />
<Icon name='search' />
<Button icon onClick={() => { setSearchFilter(''); setPaginationPage(1); setSelectedCrew(undefined); inputRef.current.focus(); }} >
<Icon name='delete' />
</Button>
</Input>
</Modal.Header>
<Modal.Content scrolling>
{renderGrid()}
</Modal.Content>
<Modal.Actions>
<Button content='Show hints' onClick={() => setShowHints(!showHints) } />
{selectedCrew && (
<Button color='blue'
content={`Guess ${selectedCrew.name}`}
onClick={() => confirmGuess(selectedCrew.symbol)} />
)}
{!selectedCrew && (
<Button content='Close' onClick={() => setModalIsOpen(false)} />
)}
</Modal.Actions>
</Modal>
);
function renderButton(): JSX.Element {
return (
<Button fluid size='big' color='blue'>
<Icon name='zoom-in' />
Guess Crew
<span style={{ fontSize: '.95em', fontWeight: 'normal', paddingLeft: '1em' }}>
({guessesLeft} guess{guessesLeft !== 1 ? 'es' : ''} remaining)
</span>
</Button>
);
}
function renderGrid(): JSX.Element {
if (!modalIsOpen) return (<></>);
let data = portalCrew.slice();
if (rules.excludedCrew.length > 0)
data = data.filter(crew => !rules.excludedCrew.includes(crew.symbol));
// Filtering
if (searchFilter !== '') {
const filter = (input: string) => input.toLowerCase().indexOf(searchFilter.toLowerCase()) >= 0;
data = data.filter(crew => filter(crew.name));
}
if (data.length === 0) return (
<Message>
<p>No crew names match your current search.</p>
<p>Only crew that are currently <b>available in the time portal</b> will be used as mystery crew and valid guesses.</p>
</Message>
);
// Pagination
const itemsPerPage = 24, itemsToShow = itemsPerPage*paginationPage;
return (
<div>
<Grid doubling columns={3} textAlign='center'>
{data.slice(0, itemsToShow).map(crew => (
<Grid.Column key={crew.symbol} style={{ cursor: 'pointer' }}
onClick={() => { if (!guesses.includes(crew.symbol)) setSelectedCrew(crew); }}
onDoubleClick={() => { if (!guesses.includes(crew.symbol)) confirmGuess(crew.symbol); }}
color={selectedCrew?.symbol === crew.symbol ? 'blue' : null}
>
<img width={48} height={48} src={`${process.env.GATSBY_ASSETS_URL}${crew.imageUrlPortrait}`} />
<div>
{guesses.includes(crew.symbol) && (<Icon name='x' color='red' />)}
{crew.name}
</div>
{!showHints && (
<div>({[crew.series.toUpperCase(), `${crew.max_rarity}*`].join(', ')})</div>
)}
</Grid.Column>
))}
</Grid>
{itemsToShow < data.length && (
<InView as='div' style={{ margin: '2em 0', textAlign: 'center' }}
onChange={(inView, entry) => { if (inView) setPaginationPage(prevState => prevState + 1); }}
>
<Icon loading name='spinner' /> Loading...
</InView>
)}
{itemsToShow >= data.length && (
<Message>Tip: Double-tap a crew to make your guess more quickly.</Message>
)}
</div>
);
}
function confirmGuess(symbol: string): void {
handleSelect(symbol);
setModalIsOpen(false);
setSelectedCrew(undefined);
}
}