react-bootstrap#Popover TypeScript Examples
The following examples show how to use
react-bootstrap#Popover.
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: CollectionSortOptions.tsx From bada-frame with GNU General Public License v3.0 | 7 votes |
CollectionSortOptions = (props: OptionProps) => {
const SortByOption = SortByOptionCreator(props);
return (
<Popover id="collection-sort-options" style={{ borderRadius: '10px' }}>
<Popover.Content
style={{ padding: 0, border: 'none', width: '185px' }}>
<ListGroup style={{ borderRadius: '8px' }}>
<SortByOption sortBy={COLLECTION_SORT_BY.LATEST_FILE}>
{constants.SORT_BY_LATEST_PHOTO}
</SortByOption>
<SortByOption sortBy={COLLECTION_SORT_BY.MODIFICATION_TIME}>
{constants.SORT_BY_MODIFICATION_TIME}
</SortByOption>
<SortByOption sortBy={COLLECTION_SORT_BY.NAME}>
{constants.SORT_BY_COLLECTION_NAME}
</SortByOption>
</ListGroup>
</Popover.Content>
</Popover>
);
}
Example #2
Source File: NoblePhantasmPopover.tsx From apps with MIT License | 5 votes |
NoblePhantasmPopover = (props: { region: Region; noblePhantasm: NoblePhantasm.NoblePhantasm }) => {
const { region, noblePhantasm } = props;
const popOverContent = (
<Popover id={`np-${noblePhantasm.id}`} className="skill-popover" lang={Manager.lang()}>
<Popover.Title>[{noblePhantasm.name}]</Popover.Title>
<Popover.Content>
<EffectBreakdown
region={region}
funcs={noblePhantasm.functions}
gain={noblePhantasm.npGain}
levels={noblePhantasm.functions[0]?.svals.length ?? 1}
popOver={true}
/>
</Popover.Content>
</Popover>
);
return (
<OverlayTrigger
trigger="click"
rootClose
placement="auto"
overlay={popOverContent}
popperConfig={{
modifiers: [
{
name: "offset",
options: {
offset: [0, 10],
},
},
],
}}
>
<Button
variant="link"
className="move-button"
title={`Click to view details of noble phantasm ${noblePhantasm.name}`}
>
[{noblePhantasm.name}]
</Button>
</OverlayTrigger>
);
}
Example #3
Source File: SkillPopover.tsx From apps with MIT License | 5 votes |
SkillPopover = (props: { region: Region; skill: Skill.Skill }) => {
const { region, skill } = props;
const popOverContent = (
<Popover id={`skill-${skill.id}`} className="skill-popover" lang={Manager.lang()}>
<Popover.Title>
<SkillDescriptor region={region} skill={skill} />
</Popover.Title>
<Popover.Content>
<EffectBreakdown
region={region}
cooldowns={skill.coolDown.length > 0 ? skill.coolDown : undefined}
funcs={skill.functions}
triggerSkillIdStack={[skill.id]}
levels={skill.functions[0]?.svals.length ?? 1}
scripts={skill.script}
popOver={true}
additionalSkillId={skill.script.additionalSkillId}
/>
</Popover.Content>
</Popover>
);
return (
<OverlayTrigger
trigger="click"
rootClose
placement="auto"
overlay={popOverContent}
popperConfig={{
modifiers: [
{
name: "offset",
options: {
offset: [0, 10],
},
},
],
}}
>
<Button variant="link" className="move-button" title={`Click to view details of skill ${skill.name}`}>
{SkillDescriptor.renderAsString(skill)}
</Button>
</OverlayTrigger>
);
}
Example #4
Source File: AddYearPopup.tsx From peterportal-client with MIT License | 5 votes |
AddYearPopup: FC<AddYearPopupProps> = ({ placeholderYear }) => {
const dispatch = useAppDispatch();
const [year, setYear] = useState(placeholderYear);
const [show, setShow] = useState(false);
const target = useRef(null);
useEffect(() => { setYear(placeholderYear) }, [placeholderYear]);
const handleClick = (event: React.MouseEvent) => {
setShow(!show);
};
return (
<div>
<Button variant="light" ref={target} className="add-year-btn" onClick={handleClick}>
<PlusCircleFill className="add-year-icon" />
<div className="add-year-text">Add year</div>
</Button>
<Overlay show={show} target={target} placement="top">
<Popover id=''>
<Popover.Content>
<Form>
<Form.Group>
<Form.Label className="add-year-form-label">
Start Year
</Form.Label>
<Form.Control
type="number"
name="year"
value={year}
onChange={(e) => {
setYear(parseInt(e.target.value));
}}
onKeyDown={(e: React.KeyboardEvent) => {
// prevent submitting form (reloads the page)
if (e.key === 'Enter') {
e.preventDefault();
}
}}
min={1000}
max={9999}
placeholder={placeholderYear.toString()}
></Form.Control>
</Form.Group>
<Button
className="popup-btn"
onClick={() => {
setShow(!show);
dispatch(addYear(
{
yearData: {
startYear: year,
quarters: ['fall', 'winter', 'spring'].map(quarter => { return { name: quarter, courses: [] } })
}
}
));
setYear(placeholderYear);
}}
>
Add Year
</Button>
</Form>
</Popover.Content>
</Popover>
</Overlay>
</div >
);
}
Example #5
Source File: Header.tsx From peterportal-client with MIT License | 5 votes |
Header: FC<HeaderProps> = ({ courseCount, unitCount, saveRoadmap }) => {
const dispatch = useAppDispatch();
const [target, setTarget] = useState<any>(null!);
const [showMenu, setShowMenu] = useState(false);
const buttons = <>
<Button variant={isMobile ? "primary" : 'light'} className={isMobile ? 'my-1' : "header-btn"} onClick={() => dispatch(setShowTransfer(true))}>
Transfer Credits
<ArrowLeftRight className="header-icon" />
</Button>
<Button variant={isMobile ? "primary" : 'light'} className={isMobile ? 'my-1' : "header-btn"} onClick={saveRoadmap}>
Save
<Save className="header-icon" />
</Button>
<Button variant={isMobile ? "primary" : 'light'} className={isMobile ? 'my-1' : "header-btn"} onClick={() => dispatch(clearPlanner())}>
Clear
<Trash className="header-icon" />
</Button>
</>
const onMenuClick = (event: React.MouseEvent) => {
setShowMenu(!showMenu);
setTarget(event.target);
};
return (
<div className="header">
<Transfer />
<div>
<div id="title">
Peter's Roadmap
</div>
<span id="planner-stats">
Total: <span id="course-count">{courseCount}</span>{" "}
{courseCount === 1 ? "course" : "courses"},{" "}
<span id="unit-count">{unitCount}</span>{" "}
{unitCount === 1 ? "unit" : "units"}
</span>
</div>
<div>
{
isMobile && <>
<Button variant="light" className="header-btn add-course" onClick={() => { dispatch(setShowSearch(true)) }}>
<Plus className="header-icon mr-1" />
Add Course
</Button>
<List className='mx-3' onClick={onMenuClick} />
<Overlay show={showMenu} target={target} placement="left">
<Popover id='roadmap-header-buttons'>
<Popover.Content>
<div className='d-flex flex-column'>
{buttons}
</div>
</Popover.Content>
</Popover>
</Overlay>
</>
}
{
isBrowser && <ButtonGroup>
{buttons}
</ButtonGroup>
}
</div>
</div >
)
}
Example #6
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 5 votes |
ProposalTransactions = ({
className,
proposalTransactions,
onRemoveProposalTransaction,
}: {
className?: string;
proposalTransactions: ProposalTransaction[];
onRemoveProposalTransaction: (index: number) => void;
}) => {
const getPopover = (tx: ProposalTransaction) => (
<Popover className={classes.popover} id="transaction-details-popover">
<Popover.Header as="h3">Transaction Details</Popover.Header>
<Popover.Body>
<Row>
<Col sm="3">
<b>Address</b>
</Col>
<Col sm="9">
<a href={buildEtherscanAddressLink(tx.address)} target="_blank" rel="noreferrer">
{tx.address}
</a>
</Col>
</Row>
<Row>
<Col sm="3">
<b>Value</b>
</Col>
<Col sm="9">{tx.value ? `${utils.formatEther(tx.value)} ETH` : 'None'}</Col>
</Row>
<Row>
<Col sm="3">
<b>Function</b>
</Col>
<Col sm="9">{tx.signature || 'None'}</Col>
</Row>
<Row>
<Col sm="3">
<b>Calldata</b>
</Col>
<Col sm="9">{tx.calldata === '0x' ? 'None' : tx.calldata}</Col>
</Row>
</Popover.Body>
</Popover>
);
return (
<div className={className}>
{proposalTransactions.map((tx, i) => (
<OverlayTrigger
key={i}
trigger={['hover', 'focus']}
placement="top"
overlay={getPopover(tx)}
>
<div
className={`${classes.transactionDetails} d-flex justify-content-between align-items-center`}
>
<div>
<span>Transaction #{i + 1} - </span>
<span>
<b>{tx.signature || 'transfer()'}</b>
</span>
</div>
<button
className={classes.removeTransactionButton}
onClick={() => onRemoveProposalTransaction(i)}
>
<img src={xIcon} alt="Remove Transaction" />
</button>
</div>
</OverlayTrigger>
))}
</div>
);
}
Example #7
Source File: VoteWidget.tsx From 3Speak-app with GNU General Public License v3.0 | 4 votes |
export function VoteWidget(props: any) {
const reflink = useMemo(() => {
return RefLink.parse(props.reflink)
}, [props.reflink])
const author = useMemo(() => {
return reflink.root
}, [reflink])
const permlink = useMemo(() => {
return reflink.permlink
}, [])
const [downvoters, setDownvoters] = useState([])
const [upvoters, setUpvoters] = useState([])
const [upvotePct, setUpvotePct] = useState(0)
const [downvotePct, setDownvotePct] = useState(0)
const [showModal, setShowModal] = useState(false)
const [showDModal, setShowDModal] = useState(false)
const setVoters = () => {
void AccountService.permalinkToPostInfo(props.reflink).then((post) => {
const votes = post.active_votes.sort((e, i) => {
return i.rshares - e.rshares
})
setDownvoters(votes.filter((vote) => vote.percent < 0))
setUpvoters(votes.filter((vote) => vote.percent >= 0).reverse())
})
}
useEffect(() => {
setVoters()
}, [])
const handleClose = () => {
setShowModal(false)
}
const handleShow = () => {
setShowModal(true)
}
const handleDClose = () => {
setShowDModal(false)
}
const handleDShow = () => {
setShowDModal(true)
}
const handleVote = async () => {
const modalState = showModal
if (modalState === false) {
handleShow()
} else {
const profileID = localStorage.getItem('SNProfileID')
if (profileID) {
try {
const profile = (await AccountService.getAccount(profileID)) as any
const theWifObj = Finder.one.in(profile.keyring).with({
privateKeys: {},
})
const wif = theWifObj.privateKeys.posting_key // posting key
const voter = profile.nickname // voting account
const weight = upvotePct // vote weight in percentage(between 1 - 100)
const accountType = 'hive'
const voteOp = {
wif,
voter,
author,
permlink,
weight,
accountType,
profileID,
}
await AccountService.voteHandler(voteOp)
NotificationManager.success('Vote cast')
const voterFmt = `@${voter}`
setUpvoters([...upvoters, voterFmt])
setShowModal(false)
upvoters.push()
} catch (error) {
NotificationManager.success('There was an error completing this operation')
}
} else {
NotificationManager.success('You need to be logged in to perform this operation')
}
}
}
const handleDownVote = async () => {
const modalState = showDModal
if (modalState === false) {
handleDShow()
} else {
const profileID = localStorage.getItem('SNProfileID')
if (profileID) {
try {
const profile = (await AccountService.getAccount(profileID)) as any
const theWifObj = Finder.one.in(profile.keyring).with({
privateKeys: {},
})
const wif = theWifObj.privateKeys.posting_key // posting key
const voter = profile.nickname // voting account
const weight = downvotePct * -1 // vote weight in percentage(between 1 - 100)
const accountType = 'hive'
const voteOp = {
wif,
voter,
author,
permlink,
weight,
accountType,
profileID,
}
await AccountService.voteHandler(voteOp)
NotificationManager.success('Vote casted, page will reload momentarily')
setShowDModal(false)
} catch (error) {
NotificationManager.success('There was an error completing this operation')
}
} else {
NotificationManager.success('You need to be logged in to perform this operation')
}
}
}
return (
<>
<span className="ml-2 p-0">
<span style={{ cursor: 'pointer' }}>
<FaThumbsUp
className="text-secondary"
onClick={() => {
void handleVote()
}}
/>
</span>
{showModal && (
<span>
<RangeSlider
value={upvotePct}
onChange={(evt) => {
setUpvotePct(evt.target.value)
}}
/>
<FontAwesomeIcon
size={'lg'}
icon={(<FaChevronCircleUp style={{ cursor: 'pointer' }} />) as any}
/>
<FontAwesomeIcon
size={'lg'}
icon={
(<FaTimesCircle style={{ cursor: 'pointer' }} className="text-danger" />) as any
}
/>
</span>
)}
</span>
<OverlayTrigger
rootClose
trigger="click"
placement="bottom"
overlay={
<Popover id="popover-basic">
<Popover.Title as="h3">
Upvotes for @{author}/{permlink}
</Popover.Title>
<Popover.Content>
{upvoters.slice(0, 10).map((e, index) => {
return (
<div key={index}>
@{e.voter}: {e.percent / 100}%<br />
</div>
)
})}
<a
onClick={() => {
//todo: open modal
}}
>
See more...
</a>
</Popover.Content>
</Popover>
}
>
<b style={{ cursor: 'pointer' }}>{upvoters.length}</b>
</OverlayTrigger>
<span className="ml-2 p-0">
<span style={{ cursor: 'pointer' }}>
<FaThumbsDown
className="text-secondary"
onClick={() => {
void handleDownVote()
}}
/>
</span>
{showDModal && (
<span>
<RangeSlider
value={downvotePct}
onChange={(changeEvent) => {
setDownvotePct(changeEvent.target.value)
}}
/>
<FontAwesomeIcon
size={'lg'}
icon={(<FaChevronCircleDown style={{ cursor: 'pointer' }} />) as any}
/>
<FontAwesomeIcon
size={'lg'}
icon={
(<FaTimesCircle style={{ cursor: 'pointer' }} className="text-danger" />) as any
}
/>
</span>
)}
</span>
<OverlayTrigger
rootClose
trigger="click"
placement="bottom"
overlay={
<Popover id="basic-popover">
<Popover.Title as="h3">
Downvotes for @{author}/{permlink}
</Popover.Title>
<Popover.Content>
{downvoters.slice(0, 10).map((item, index) => {
return (
<div key={index}>
@{item.voter}: {item.percent / 100}%<br />
</div>
)
})}
<a
onClick={() => {
//todo: open modal
}}
>
See more...
</a>
</Popover.Content>
</Popover>
}
>
<b style={{ cursor: 'pointer' }}>{downvoters.length}</b>
</OverlayTrigger>
</>
)
}
Example #8
Source File: CollectionOptions.tsx From bada-frame with GNU General Public License v3.0 | 4 votes |
CollectionOptions = (props: CollectionOptionsProps) => {
const collectionRename = async (
selectedCollection: Collection,
newName: string
) => {
if (selectedCollection.name !== newName) {
await renameCollection(selectedCollection, newName);
props.syncWithRemote();
}
};
const showRenameCollectionModal = () => {
props.setCollectionNamerAttributes({
title: constants.RENAME_COLLECTION,
buttonText: constants.RENAME,
autoFilledName: getSelectedCollection(
props.selectedCollectionID,
props.collections
)?.name,
callback: (newName) => {
props.startLoading();
collectionRename(
getSelectedCollection(
props.selectedCollectionID,
props.collections
),
newName
);
},
});
};
const confirmDeleteCollection = () => {
props.setDialogMessage({
title: constants.CONFIRM_DELETE_COLLECTION,
content: constants.DELETE_COLLECTION_MESSAGE(),
staticBackdrop: true,
proceed: {
text: constants.DELETE_COLLECTION,
action: () => {
props.startLoading();
deleteCollection(
props.selectedCollectionID,
props.syncWithRemote,
props.redirectToAll,
props.setDialogMessage
);
},
variant: 'danger',
},
close: {
text: constants.CANCEL,
},
});
};
const archiveCollectionHelper = () => {
changeCollectionVisibilityHelper(
getSelectedCollection(
props.selectedCollectionID,
props.collections
),
props.startLoading,
props.finishLoading,
props.setDialogMessage,
props.syncWithRemote
);
};
const confirmDownloadCollection = () => {
props.setDialogMessage({
title: constants.CONFIRM_DOWNLOAD_COLLECTION,
content: constants.DOWNLOAD_COLLECTION_MESSAGE(),
staticBackdrop: true,
proceed: {
text: constants.DOWNLOAD,
action: downloadCollectionHelper,
variant: 'success',
},
close: {
text: constants.CANCEL,
},
});
};
const downloadCollectionHelper = async () => {
props.startLoading();
await downloadCollection(
props.selectedCollectionID,
props.setDialogMessage
);
await sleep(1000);
props.finishLoading();
};
return (
<Popover id="collection-options" style={{ borderRadius: '10px' }}>
<Popover.Content style={{ padding: 0, border: 'none' }}>
<ListGroup style={{ borderRadius: '8px' }}>
<MenuItem>
<MenuLink onClick={showRenameCollectionModal}>
{constants.RENAME}
</MenuLink>
</MenuItem>
<MenuItem>
<MenuLink onClick={props.showCollectionShareModal}>
{constants.SHARE}
</MenuLink>
</MenuItem>
<MenuItem>
<MenuLink onClick={confirmDownloadCollection}>
{constants.DOWNLOAD}
</MenuLink>
</MenuItem>
<MenuItem>
<MenuLink onClick={archiveCollectionHelper}>
{IsArchived(
getSelectedCollection(
props.selectedCollectionID,
props.collections
)
)
? constants.UNARCHIVE
: constants.ARCHIVE}
</MenuLink>
</MenuItem>
<MenuItem>
<MenuLink
variant={ButtonVariant.danger}
onClick={confirmDeleteCollection}>
{constants.DELETE}
</MenuLink>
</MenuItem>
</ListGroup>
</Popover.Content>
</Popover>
);
}
Example #9
Source File: Quarter.tsx From peterportal-client with MIT License | 4 votes |
Quarter: FC<QuarterProps> = ({ year, yearIndex, quarterIndex, data }) => {
const dispatch = useAppDispatch();
let quarterTitle = data.name.charAt(0).toUpperCase() + data.name.slice(1);
const invalidCourses = useAppSelector(state => state.roadmap.invalidCourses);
const [showQuarterMenu, setShowQuarterMenu] = useState(false);
const [target, setTarget] = useState<any>(null!);
const handleQuarterMenuClick = (event: React.MouseEvent) => {
setShowQuarterMenu(!showQuarterMenu);
setTarget(event.target);
}
const calculateQuarterStats = () => {
let unitCount = 0;
let courseCount = 0;
data.courses.forEach(course => {
unitCount += course.units[0];
courseCount += 1;
})
return [unitCount, courseCount];
};
let unitCount = calculateQuarterStats()[0];
const renderCourses = () => {
return data.courses.map((course, index) => {
return <Draggable key={`quarter-course-${index}`} draggableId={`${yearIndex}-${quarterIndex}-${course.id}-${index}`} index={index}>
{(provided, snapshot) => {
let requiredCourses: string[] = null!;
// if this is an invalid course, set the required courses
invalidCourses.forEach(ic => {
let loc = ic.location;
if (loc.courseIndex == index && loc.quarterIndex == quarterIndex && loc.yearIndex == yearIndex) {
requiredCourses = ic.required;
}
});
const onDelete = () => {
dispatch(deleteCourse({
yearIndex,
quarterIndex,
courseIndex: index,
}));
};
return (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
margin: '0rem 2rem 1rem 2rem',
...provided.draggableProps.style
}}
>
<Course key={course.id} {...course}
requiredCourses={requiredCourses}
onDelete={onDelete} />
</div>
);
}}
</Draggable>
})
}
return <div className="quarter">
<span className="quarter-header">
<h2 className="quarter-title">
{quarterTitle} {year}
</h2>
<ThreeDots onClick={handleQuarterMenuClick} className="edit-btn" />
<Overlay show={showQuarterMenu} target={target} placement="bottom">
<Popover id={`quarter-menu-${yearIndex}-${quarterIndex}`}>
<Popover.Content>
<div>
<Button variant="light" className="quarter-menu-btn red-menu-btn" onClick={() => dispatch(clearQuarter({ yearIndex: yearIndex, quarterIndex: quarterIndex }))}>
Clear
</Button>
<Button variant="light" className="quarter-menu-btn red-menu-btn" onClick={() => {
dispatch(deleteQuarter({ yearIndex: yearIndex, quarterIndex: quarterIndex }));
setShowQuarterMenu(false)
}}>
Delete
</Button>
</div>
</Popover.Content>
</Popover>
</Overlay>
</span>
<div className="quarter-units">
{unitCount} {unitCount === 1 ? "unit" : "units"}
</div>
<Droppable droppableId={yearIndex + "-" + quarterIndex} type="COURSE">
{(provided) => {
return (
<div
ref={provided.innerRef}
{...provided.droppableProps}
style={{ paddingBottom: '1rem' }}>
{renderCourses()}
{provided.placeholder}
</div>
);
}}
</Droppable>
</div>
}
Example #10
Source File: Year.tsx From peterportal-client with MIT License | 4 votes |
Year: FC<YearProps> = ({ yearIndex, data }) => {
const dispatch = useAppDispatch();
const [showContent, setShowContent] = useState(true);
const [show, setShow] = useState(false);
const [showAddQuarter, setShowAddQuarter] = useState(false);
const [showEditYear, setShowEditYear] = useState(false);
const [target, setTarget] = useState<any>(null!);
const [addQuarterTarget, setAddQuarterTarget] = useState<any>(null!);
const [editYearTarget, setEditYearTarget] = useState<any>(null!);
const [placeholderYear, setPlaceholderYear] = useState(data.startYear);
const handleEditClick = (event: React.MouseEvent) => {
if (showAddQuarter) {
/* hide both overlays */
setShowAddQuarter(!showAddQuarter);
setShow(!show);
} else if (showEditYear) {
setShowEditYear(!showEditYear);
setShow(!show);
} else {
setShow(!show);
setTarget(event.target);
}
};
const handleShowAddQuarterClick = (event: React.MouseEvent) => {
setShowEditYear(false); // hide any other currently displayed menu bar options
setShowAddQuarter(!showAddQuarter);
setAddQuarterTarget(event.target);
}
const handleAddQuarterClick = (year: number, quarter: string) => {
dispatch(addQuarter({ startYear: year, quarterData: { name: quarter, courses: [] } }));
}
const handleEditYearClick = (event: React.MouseEvent) => {
setShowAddQuarter(false); // hide any other currently displayed menu bar options
setPlaceholderYear(data.startYear); // set default year to current year
setShowEditYear(!showEditYear);
setEditYearTarget(event.target);
}
const calculateYearStats = () => {
let unitCount = 0;
let courseCount = 0;
data.quarters.forEach(quarter => {
quarter.courses.forEach(course => {
unitCount += course.units[0];
courseCount += 1;
})
})
return { unitCount, courseCount };
};
let { unitCount, courseCount } = calculateYearStats();
return (
<div className="year">
<div className="yearTitleBar">
<Button
variant="link"
className="year-accordion"
onClick={() => {
setShowContent(!showContent);
}}
>
<span className="year-accordion-title">
<span id="year-title">
{showContent ? (
<CaretDownFill className="caret-icon" />
) : (
<CaretRightFill className="caret-icon" />
)}
<span id="year-number">Year {yearIndex + 1} </span>
<span id="year-range">
({data.startYear} - {data.startYear + 1})
</span>
</span>
<span id="year-stats">
<span id="course-count">{courseCount}</span>{" "}
{courseCount === 1 ? "course" : "courses"},{" "}
<span id="unit-count">{unitCount}</span>{" "}
{unitCount === 1 ? "unit" : "units"}
</span>
</span>
</Button>
<ThreeDots onClick={handleEditClick} className="edit-btn" />
<Overlay show={show} target={target} placement="bottom">
<Popover id={`year-menu-${yearIndex}`}>
<Popover.Content className="year-settings-popup">
<div>
<Button disabled={!(data.quarters && data.quarters.length < 6)} onClick={handleShowAddQuarterClick} variant="light" className="year-settings-btn">
Add Quarter
</Button>
<Button onClick={handleEditYearClick} variant="light" className="year-settings-btn">
Edit Year
</Button>
<Button
variant="light"
className="year-settings-btn"
id="clear-btn"
onClick={() => {
dispatch(clearYear({
yearIndex: yearIndex
}));
}}
>
Clear
</Button>
<Button
variant="light"
className="year-settings-btn"
id="remove-btn"
onClick={() => {
dispatch(deleteYear({
yearIndex: yearIndex
}));
}}
>
Remove
</Button>
</div>
</Popover.Content>
</Popover>
</Overlay>
<Overlay show={showAddQuarter && data.quarters && data.quarters.length < 6} target={addQuarterTarget} placement="left">
<Popover id={`add-quarter-menu-${yearIndex}`}>
<Popover.Content>
<div>
{!data.quarters.map(quarter => quarter.name).includes("fall") && <Button onClick={() => handleAddQuarterClick(data.startYear, "fall")} variant="light" className="year-settings-btn">Fall</Button>}
{!data.quarters.map(quarter => quarter.name).includes("winter") && <Button onClick={() => handleAddQuarterClick(data.startYear, "winter")} variant="light" className="year-settings-btn">Winter</Button>}
{!data.quarters.map(quarter => quarter.name).includes("spring") && <Button onClick={() => handleAddQuarterClick(data.startYear, "spring")} variant="light" className="year-settings-btn">Spring</Button>}
{!data.quarters.map(quarter => quarter.name).includes("summer I") && <Button onClick={() => handleAddQuarterClick(data.startYear, "summer I")} variant="light" className="year-settings-btn">Summer I</Button>}
{!data.quarters.map(quarter => quarter.name).includes("summer II") && <Button onClick={() => handleAddQuarterClick(data.startYear, "summer II")} variant="light" className="year-settings-btn">Summer II</Button>}
{!data.quarters.map(quarter => quarter.name).includes("summer 10 Week") && <Button onClick={() => handleAddQuarterClick(data.startYear, "summer 10 Week")} variant="light" className="year-settings-btn">Summer 10 Week</Button>}
</div>
</Popover.Content>
</Popover>
</Overlay>
<Overlay show={showEditYear} target={editYearTarget} placement="left">
<Popover id={`edit-year-menu-${yearIndex}`}>
<Popover.Content>
<Form>
<Form.Group>
<Form.Label className="edit-year-form-label">
Start Year
</Form.Label>
<Form.Control
type="number"
name="year"
value={placeholderYear}
onChange={(e) => {
setPlaceholderYear(parseInt(e.target.value));
}}
onKeyDown={(e: React.KeyboardEvent) => {
// prevent submitting form (reloads the page)
if (e.key === 'Enter') {
e.preventDefault();
}
}}
min={1000}
max={9999}
placeholder={placeholderYear.toString()}
></Form.Control>
</Form.Group>
<Button
className="edit-year-popup-btn"
onClick={() => {
setShowEditYear(!showEditYear);
setShow(!show);
if (placeholderYear != data.startYear) {
dispatch(editYear({ startYear: placeholderYear, index: yearIndex }));
}
}}
>
Confirm
</Button>
</Form>
</Popover.Content>
</Popover>
</Overlay>
</div>
{showContent && (
<div className="year-accordion-content">
{
data.quarters.map((quarter, quarterIndex) => {
return <Quarter
key={`year-quarter-${quarterIndex}`}
year={data.startYear + (quarterIndex == 0 ? 0 : 1)}
yearIndex={yearIndex}
quarterIndex={quarterIndex}
data={quarter}
/>
})
}
{/* render blank, non-functional quarters to ensure there are 3 per row */}
{data.quarters.length > 3 && data.quarters.length < 6 && (
[undefined, undefined].slice(data.quarters.length - 4).map(() => {
return <div className="empty-quarter"></div>
})
)
}
</div>
)}
</div>
);
}
Example #11
Source File: Menu.tsx From cftracker with MIT License | 4 votes |
Menu = (): JSX.Element => {
const dispatch = useDispatch();
const state: RootStateType = useSelector((state) => state) as RootStateType;
const [handle, setHandle] = useState(
state.userList.handles.length ? state.userList.handles.toString() : ""
);
console.log(state.userList.handles.toString());
useEffect(() => {
fetchProblemList(dispatch);
fetchContestList(dispatch);
fetchSharedProblemList(dispatch);
}, []);
// useEffect(() => {
// if (!state.contestList.loading && !state.problemList.loading) sync(true);
// }, [state.userList]);
useEffect(() => {
if (!state.contestList.loading && !state.problemList.loading)
sync(state.userList.handles.length > 2 ? true : false);
// console.log(state.contestList.loading);
// console.log(state.problemList.loading);
}, [state.userList, state.contestList.loading, state.problemList.loading]);
const sync = (wait = false) => {
fetchUserSubmissions(dispatch, state.userList.handles, wait);
};
const submitUser = () => {
// Notification.info({
// title: "User submitted!",
// duration: 200,
// description: "hh",
// });
// toast.error("? Wow so easy!", {
// position: "bottom-right",
// autoClose: 2001,
// hideProgressBar: false,
// closeOnClick: true,
// pauseOnHover: true,
// draggable: true,
// progress: undefined,
// });
fetchUsers(dispatch, handle);
};
return (
<Navbar
className={
"navbar navbar-expand-lg p-2 ps-4 pe-4 " + state.appState.theme.navbar
}
expand="md"
>
<div className="container p-0">
<Link to="/" className="navbar-brand" href="#">
CFTracker
</Link>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ms-auto mt-2 mt-lg-0">
<li className="nav-item active">
<Link to={Path.Issues} className="nav-link" href="#">
{/* <span className="p-1">{<FontAwesomeIcon icon={faBars} />}</span> */}
<span>Issues</span>
</Link>
</li>
<li className="nav-item active">
<Link to={Path.PROBLEMS} className="nav-link" href="#">
{/* <span className="p-1">{<FontAwesomeIcon icon={faBars} />}</span> */}
<span>Problems</span>
</Link>
</li>
<li className="nav-item">
<Link to={Path.CONTESTS} className="nav-link" href="#">
{/* <span className="p-1"> {<FontAwesomeIcon icon={faListAlt} />} </span>*/}
<span>Contests</span>
</Link>
</li>
<li className="nav-item">
<OverlayTrigger
trigger="click"
placement="bottom"
key="bottom"
overlay={
<Popover
id="popover-basic"
className={state.appState.theme.bgText}
>
<Popover.Header
as="h3"
className={state.appState.theme.bgText}
>
<div className="d-flex align-items-center">
<span className={state.appState.theme.bgText}>
CFTracker (Created by{" "}
<a
href="https://codeforces.com/profile/bashem"
className={" " + state.appState.theme.text}
target="__blank"
>
bashem
</a>
)
</span>
</div>
</Popover.Header>
<Popover.Body className={state.appState.theme.bgText}>
<ul className="list-group list-group-flush">
<li
className={
"list-group-item " + state.appState.theme.bgText
}
>
<span className="pe-2">Source Code</span>
<a
href="https://github.com/mbashem/cftracker"
className="text-secondary pt-1 fs-5"
target="__blank"
>
{<FontAwesomeIcon icon={faGithub} />}
</a>
</li>
</ul>
</Popover.Body>
</Popover>
}
>
<a
href="#"
onClick={(e) => e.preventDefault()}
className="nav-link"
title="Created by Bashem"
>
<FontAwesomeIcon icon={faInfo} />
</a>
</OverlayTrigger>
</li>
<li className="nav-item">
<a
className={"nav-link"}
href="#"
title="Change Theme"
onClick={(e) => {
e.preventDefault();
if (state.appState.themeMod === ThemesType.DARK)
changeAppState(
dispatch,
AppReducerType.CHANGE_THEME,
ThemesType.LIGHT
);
else
changeAppState(
dispatch,
AppReducerType.CHANGE_THEME,
ThemesType.DARK
);
}}
>
<FontAwesomeIcon
icon={
state.appState.themeMod === ThemesType.DARK ? faMoon : faSun
}
/>
</a>
</li>
<li className="nav-item">
<a
className="nav-link"
onClick={(e) => {
e.preventDefault();
sync();
}}
title="Refresh Submissions"
href="#"
>
<FontAwesomeIcon icon={faSync} />
</a>
</li>
<li className="nav-item">
<form
className="form-inline d-flex my-2 my-lg-0 nav-item"
onSubmit={(e) => {
e.preventDefault();
submitUser();
}}
>
<input
name="handle"
className={"form-control " + state.appState.theme.bgText}
type="text"
placeholder="handle1,handle2,.."
aria-label="handles"
value={handle}
onChange={(e) => setHandle(e.target.value)}
/>
</form>
</li>
</Nav>
</Navbar.Collapse>
</div>
</Navbar>
);
}
Example #12
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 4 votes |
Playground: React.FC = () => {
const [nounSvgs, setNounSvgs] = useState<string[]>();
const [traits, setTraits] = useState<Trait[]>();
const [modSeed, setModSeed] = useState<{ [key: string]: number }>();
const [initLoad, setInitLoad] = useState<boolean>(true);
const [displayNoun, setDisplayNoun] = useState<boolean>(false);
const [indexOfNounToDisplay, setIndexOfNounToDisplay] = useState<number>();
const [selectIndexes, setSelectIndexes] = useState<Record<string, number>>({});
const [pendingTrait, setPendingTrait] = useState<PendingCustomTrait>();
const [isPendingTraitValid, setPendingTraitValid] = useState<boolean>();
const customTraitFileRef = useRef<HTMLInputElement>(null);
const generateNounSvg = React.useCallback(
(amount: number = 1) => {
for (let i = 0; i < amount; i++) {
const seed = { ...getRandomNounSeed(), ...modSeed };
const { parts, background } = getNounData(seed);
const svg = buildSVG(parts, encoder.data.palette, background);
setNounSvgs(prev => {
return prev ? [svg, ...prev] : [svg];
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[pendingTrait, modSeed],
);
useEffect(() => {
const traitTitles = ['background', 'body', 'accessory', 'head', 'glasses'];
const traitNames = [
['cool', 'warm'],
...Object.values(ImageData.images).map(i => {
return i.map(imageData => imageData.filename);
}),
];
setTraits(
traitTitles.map((value, index) => {
return {
title: value,
traitNames: traitNames[index],
};
}),
);
if (initLoad) {
generateNounSvg(8);
setInitLoad(false);
}
}, [generateNounSvg, initLoad]);
const traitOptions = (trait: Trait) => {
return Array.from(Array(trait.traitNames.length + 1)).map((_, index) => {
const traitName = trait.traitNames[index - 1];
const parsedTitle = index === 0 ? `Random` : parseTraitName(traitName);
return (
<option key={index} value={traitName}>
{parsedTitle}
</option>
);
});
};
const traitButtonHandler = (trait: Trait, traitIndex: number) => {
setModSeed(prev => {
// -1 traitIndex = random
if (traitIndex < 0) {
let state = { ...prev };
delete state[trait.title];
return state;
}
return {
...prev,
[trait.title]: traitIndex,
};
});
};
const resetTraitFileUpload = () => {
if (customTraitFileRef.current) {
customTraitFileRef.current.value = '';
}
};
let pendingTraitErrorTimeout: NodeJS.Timeout;
const setPendingTraitInvalid = () => {
setPendingTraitValid(false);
resetTraitFileUpload();
pendingTraitErrorTimeout = setTimeout(() => {
setPendingTraitValid(undefined);
}, 5_000);
};
const validateAndSetCustomTrait = (file: File | undefined) => {
if (pendingTraitErrorTimeout) {
clearTimeout(pendingTraitErrorTimeout);
}
if (!file) {
return;
}
const reader = new FileReader();
reader.onload = e => {
try {
const buffer = Buffer.from(e?.target?.result!);
const png = PNG.sync.read(buffer);
if (png.width !== 32 || png.height !== 32) {
throw new Error('Image must be 32x32');
}
const filename = file.name?.replace('.png', '') || 'custom';
const data = encoder.encodeImage(filename, {
width: png.width,
height: png.height,
rgbaAt: (x: number, y: number) => {
const idx = (png.width * y + x) << 2;
const [r, g, b, a] = [
png.data[idx],
png.data[idx + 1],
png.data[idx + 2],
png.data[idx + 3],
];
return {
r,
g,
b,
a,
};
},
});
setPendingTrait({
data,
filename,
type: DEFAULT_TRAIT_TYPE,
});
setPendingTraitValid(true);
} catch (error) {
setPendingTraitInvalid();
}
};
reader.readAsArrayBuffer(file);
};
const uploadCustomTrait = () => {
const { type, data, filename } = pendingTrait || {};
if (type && data && filename) {
const images = ImageData.images as Record<string, EncodedImage[]>;
images[type].unshift({
filename,
data,
});
const title = traitKeyToTitle[type];
const trait = traits?.find(t => t.title === title);
resetTraitFileUpload();
setPendingTrait(undefined);
setPendingTraitValid(undefined);
traitButtonHandler(trait!, 0);
setSelectIndexes({
...selectIndexes,
[title]: 0,
});
}
};
return (
<>
{displayNoun && indexOfNounToDisplay !== undefined && nounSvgs && (
<NounModal
onDismiss={() => {
setDisplayNoun(false);
}}
svg={nounSvgs[indexOfNounToDisplay]}
/>
)}
<Container fluid="lg">
<Row>
<Col lg={10} className={classes.headerRow}>
<span>
<Trans>Explore</Trans>
</span>
<h1>
<Trans>Playground</Trans>
</h1>
<p>
<Trans>
The playground was built using the {nounsProtocolLink}. Noun's traits are determined
by the Noun Seed. The seed was generated using {nounsAssetsLink} and rendered using
the {nounsSDKLink}.
</Trans>
</p>
</Col>
</Row>
<Row>
<Col lg={3}>
<Col lg={12}>
<Button
onClick={() => {
generateNounSvg();
}}
className={classes.primaryBtn}
>
<Trans>Generate Nouns</Trans>
</Button>
</Col>
<Row>
{traits &&
traits.map((trait, index) => {
return (
<Col lg={12} xs={6}>
<Form className={classes.traitForm}>
<FloatingLabel
controlId="floatingSelect"
label={traitKeyToLocalizedTraitKeyFirstLetterCapitalized(trait.title)}
key={index}
className={classes.floatingLabel}
>
<Form.Select
aria-label="Floating label select example"
className={classes.traitFormBtn}
value={trait.traitNames[selectIndexes?.[trait.title]] ?? -1}
onChange={e => {
let index = e.currentTarget.selectedIndex;
traitButtonHandler(trait, index - 1); // - 1 to account for 'random'
setSelectIndexes({
...selectIndexes,
[trait.title]: index - 1,
});
}}
>
{traitOptions(trait)}
</Form.Select>
</FloatingLabel>
</Form>
</Col>
);
})}
</Row>
<label style={{ margin: '1rem 0 .25rem 0' }} htmlFor="custom-trait-upload">
<Trans>Upload Custom Trait</Trans>
<OverlayTrigger
trigger="hover"
placement="top"
overlay={
<Popover>
<div style={{ padding: '0.25rem' }}>
<Trans>Only 32x32 PNG images are accepted</Trans>
</div>
</Popover>
}
>
<Image
style={{ margin: '0 0 .25rem .25rem' }}
src={InfoIcon}
className={classes.voteIcon}
/>
</OverlayTrigger>
</label>
<Form.Control
type="file"
id="custom-trait-upload"
accept="image/PNG"
isValid={isPendingTraitValid}
isInvalid={isPendingTraitValid === false}
ref={customTraitFileRef}
className={classes.fileUpload}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
validateAndSetCustomTrait(e.target.files?.[0])
}
/>
{pendingTrait && (
<>
<FloatingLabel label="Custom Trait Type" className={classes.floatingLabel}>
<Form.Select
aria-label="Custom Trait Type"
className={classes.traitFormBtn}
onChange={e => setPendingTrait({ ...pendingTrait, type: e.target.value })}
>
{Object.entries(traitKeyToTitle).map(([key, title]) => (
<option value={key}>{capitalizeFirstLetter(title)}</option>
))}
</Form.Select>
</FloatingLabel>
<Button onClick={() => uploadCustomTrait()} className={classes.primaryBtn}>
<Trans>Upload</Trans>
</Button>
</>
)}
<p className={classes.nounYearsFooter}>
<Trans>
You've generated{' '}
{i18n.number(parseInt(nounSvgs ? (nounSvgs.length / 365).toFixed(2) : '0'))} years
worth of Nouns
</Trans>
</p>
</Col>
<Col lg={9}>
<Row>
{nounSvgs &&
nounSvgs.map((svg, i) => {
return (
<Col xs={4} lg={3} key={i}>
<div
onClick={() => {
setIndexOfNounToDisplay(i);
setDisplayNoun(true);
}}
>
<Noun
imgPath={`data:image/svg+xml;base64,${btoa(svg)}`}
alt="noun"
className={classes.nounImg}
wrapperClassName={classes.nounWrapper}
/>
</div>
</Col>
);
})}
</Row>
</Col>
</Row>
</Container>
</>
);
}