react-bootstrap#Image TypeScript Examples
The following examples show how to use
react-bootstrap#Image.
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: BackgroundLogo.tsx From advocacy-maps with MIT License | 6 votes |
export default function BackgroundLogo() {
return (
<div
style={{
position: "absolute",
right: 0,
bottom: 0,
transformOrigin: "bottom right",
transform: "scale(1.4)"
}}
>
<Image fluid alt="" src="maple-1.png" />
</div>
)
}
Example #2
Source File: TestimonyCallout.tsx From advocacy-maps with MIT License | 6 votes |
VoteHand = ({ position }: { position: Testimony["position"] }) => {
return (
<Image
className={`${position === "endorse" ? styles.flip : ""} ${
position === "neutral" ? styles.flipRotate : ""
}`}
alt={`${position}`}
src="VoteHand.png"
style={{ margin: "-1em" }}
/>
)
}
Example #3
Source File: notification-page.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 6 votes |
NotificationPage = (props: any) => {
const { t } = useTranslation();
const [show, setShow] = React.useState(true);
React.useEffect(() => {
if (props)
setShow(props.show);
}, [props, props.show])
return (
<>
<Modal
dialogClassName='align-items-end'
contentClassName='border-0 bg-transparent'
show={show}
backdrop={false}
keyboard={false}
centered
onEnter={() => setTimeout(props.setNotificationShow, 3000, false)}
>
<Alert className='qt-notification' variant='success' onClose={() => props.setNotificationShow(false)}>
<Image src={successIcon} className='mr-3 my-3' />
<p className='qt-notification-text my-auto mx-1'>
{t('translation:successfull-transferred')}
</p>
</Alert>
</Modal>
</>
)
}
Example #4
Source File: notification-toast.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 6 votes |
NotificationToast = (props: any) => {
const { t } = useTranslation();
return (
<div className='toast-container'>
<Toast className='qt-notification'
show={props.show}
delay={3000}
autohide
onClose={() => props.setNotificationShow(false)}
>
<ToastHeader closeButton={false} className='qt-notification-header'>
<Image src={successIcon} className='mr-3 my-3' />
<p className='qt-notification-text my-auto mx-1'>
{t('translation:successfull-transferred')}
</p>
</ToastHeader>
</Toast>
</div>
)
}
Example #5
Source File: footer.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 6 votes |
Footer = (props: any) => { const { t } = useTranslation(); const handleDataPrivacyClick = () => { props.setDataPrivacyShow(true) } const handleImprintClick = () => { props.setImprintShow(true) } return ( // simple footer with imprint and data privacy --> links tbd <Row id='qt-footer'> <Button variant='link' className="my-0 p-0 mx-5 footer-font" onClick={handleImprintClick}> {t('translation:imprint')} </Button> <Image className="my-auto" src={DataProtectLogo} /> <Button variant='link' className="my-0 p-0 mx-2 footer-font" onClick={handleDataPrivacyClick}> {t('translation:data-privacy')} </Button> </Row> ) }
Example #6
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 6 votes |
NounInfoRowBirthday: React.FC<NounInfoRowBirthdayProps> = props => {
const { nounId } = props;
// If the noun is a nounder noun, use the next noun to get the mint date.
// We do this because we use the auction start time to get the mint date and
// nounder nouns do not have an auction start time.
const nounIdForQuery = isNounderNoun(BigNumber.from(nounId)) ? nounId + 1 : nounId;
const pastAuctions = useAppSelector(state => state.pastAuctions.pastAuctions);
if (!pastAuctions || !pastAuctions.length) {
return <></>;
}
const startTime = BigNumber.from(
pastAuctions.find((auction: AuctionState, i: number) => {
const maybeNounId = auction.activeAuction?.nounId;
return maybeNounId ? BigNumber.from(maybeNounId).eq(BigNumber.from(nounIdForQuery)) : false;
})?.activeAuction?.startTime || 0,
);
if (!startTime) {
return <Trans>Error fetching Noun birthday</Trans>;
}
const birthday = new Date(Number(startTime._hex) * 1000);
return (
<div className={classes.birthdayInfoContainer}>
<span>
<Image src={_BirthdayIcon} className={classes.birthdayIcon} />
</span>
<Trans>Born</Trans>
<span className={classes.nounInfoRowBirthday}>
{i18n.date(birthday, { month: 'long', year: 'numeric', day: '2-digit' })}
</span>
</div>
);
}
Example #7
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 6 votes |
NounInfoRowButton: React.FC<NounInfoRowButtonProps> = props => {
const { iconImgSource, btnText, onClickHandler } = props;
const isCool = useAppSelector(state => state.application.isCoolBackground);
return (
<div
className={isCool ? classes.nounButtonCool : classes.nounButtonWarm}
onClick={onClickHandler}
>
<div className={classes.nounButtonContents}>
<Image src={iconImgSource} className={classes.buttonIcon} />
{btnText}
</div>
</div>
);
}
Example #8
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 6 votes |
selectIconForNounVoteActivityRow = (proposal: Proposal, vote?: NounVoteHistory) => {
if (!vote) {
if (proposal.status === ProposalState.PENDING || proposal.status === ProposalState.ACTIVE) {
return <Image src={_PendingVoteIcon} className={classes.voteIcon} />;
}
return <Image src={_AbsentVoteIcon} className={classes.voteIcon} />;
} else if (vote.supportDetailed) {
switch (vote.supportDetailed) {
case Vote.FOR:
return <Image src={_YesVoteIcon} className={classes.voteIcon} />;
case Vote.ABSTAIN:
default:
return <Image src={_AbstainVoteIcon} className={classes.voteIcon} />;
}
} else {
return <Image src={_NoVoteIcon} className={classes.voteIcon} />;
}
}
Example #9
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 6 votes |
NotFoundPage = () => {
return (
<Section fullWidth={false}>
<Col lg={4}>
<Image src={_404img} fluid />
</Col>
<Col lg={8}>
<h1 className={classes.heading}>
<Trans>404: This is not the person, place, or thing you're looking for...</Trans>
</h1>
</Col>
</Section>
);
}
Example #10
Source File: disclamer-btn.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 5 votes |
DisclamerButton = (props: any) => {
const { t } = useTranslation();
const [show, setShow] = React.useState(false);
React.useEffect(() => {
setShow(props.firstTimeShow);
props.onInit();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<>
<Image
src={SpeechBubbleImage}
className='speech-bubble'
onClick={() => { setShow(true) }}
/>
<Modal
contentClassName='data-modal'
show={show}
backdrop={true}
onHide={() => { setShow(false) }}
keyboard={false}
centered
>
<Modal.Header id='data-header' className='pb-0' >
<Row>
<Col >
<Card.Title className='m-0 jcc-xs-jcfs-md' as={'h2'} >
{t('translation:disclaimer-title')}</Card.Title>
</Col>
</Row>
</Modal.Header>
<Modal.Body className='bg-light py-0'>
<hr />
<h5 className='disclaimer-text'>
<Trans>{props.disclaimerText}</Trans>
</h5>
<hr />
<FormGroupConsentCkb controlId='formDoNotShowCheckbox' title={t('translation:disclaimer-do-not-show')}
onChange={(evt: any) => props.onCheckChange(evt.currentTarget.checked)}
type='checkbox'
checked={props.checked}
/>
</Modal.Body>
<Modal.Footer id='data-footer'>
<Container className='p-0'>
<Row className='justify-content-end'>
<Col xs='6' className='p-0'>
<Button
className='py-0'
block
onClick={() => { setShow(false) }}
>
{t('translation:ok')}
</Button>
</Col>
</Row>
</Container>
</Modal.Footer>
</Modal>
</>
)
}
Example #11
Source File: index.tsx From nouns-monorepo with GNU General Public License v3.0 | 5 votes |
NounInfoRowHolder: React.FC<NounInfoRowHolderProps> = props => {
const { nounId } = props;
const isCool = useAppSelector(state => state.application.isCoolBackground);
const { loading, error, data } = useQuery(nounQuery(nounId.toString()));
const etherscanURL = buildEtherscanAddressLink(data && data.noun.owner.id);
if (loading) {
return (
<div className={classes.nounHolderInfoContainer}>
<span className={classes.nounHolderLoading}>
<Trans>Loading...</Trans>
</span>
</div>
);
} else if (error) {
return (
<div>
<Trans>Failed to fetch Noun info</Trans>
</div>
);
}
const shortAddressComponent = <ShortAddress address={data && data.noun.owner.id} />;
return (
<div className={classes.nounHolderInfoContainer}>
<span>
<Image src={_HeartIcon} className={classes.heartIcon} />
</span>
<span>
<Trans>Held by</Trans>
</span>
<span>
<a
className={
isCool ? classes.nounHolderEtherscanLinkCool : classes.nounHolderEtherscanLinkWarm
}
href={etherscanURL}
target={'_blank'}
rel="noreferrer"
>
{data.noun.owner.id.toLowerCase() ===
config.addresses.nounsAuctionHouseProxy.toLowerCase() ? (
<Trans>Nouns Auction House</Trans>
) : (
shortAddressComponent
)}
</a>
</span>
<span className={classes.linkIconSpan}>
<Image src={_LinkIcon} className={classes.linkIcon} />
</span>
</div>
);
}
Example #12
Source File: header.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 4 votes |
Header = (props: any) => {
const navigation = useNavigation();
const history = useHistory();
const { t } = useTranslation();
const { keycloak } = useKeycloak();
const [userName, setUserName] = React.useState('');
const [isInit, setIsInit] = React.useState(false)
const [environmentName] = useLocalStorage('environmentName', '');
React.useEffect(() => {
if (navigation)
setIsInit(true);
}, [navigation])
// set user name from keycloak
React.useEffect(() => {
if (keycloak.idTokenParsed) {
setUserName((keycloak.idTokenParsed as any).name);
}
}, [keycloak])
const handleLogout = () => {
keycloak.logout({ redirectUri: window.location.origin + navigation!.calculatedRoutes.landing });
}
const changePasswordUrl = keycloak.authServerUrl + 'realms/' + keycloak.realm + '/account/password';
return (!isInit ? <></> :
<Container className='position-relative'>
{/* simple header with logo */}
{/* user icon and user name */}
<Row id='qt-header'>
<Image id='c19-logo' src={C19Logo} />
<span className='header-font my-auto mx-1 pt-1'>
{t('translation:title')}
{!environmentName
? <></>
: <span className='environment-font my-auto mx-1'>
{'\n' + environmentName}
</span>
}
</span>
{!(environmentName && history.location.pathname === navigation?.routes.root)
? <></>
: < span className='environment-info-text py-3'>
<Trans>
{t('translation:environment-info1')}
{<a
href={t('translation:environment-info-link')}
target='blank'
>
{t('translation:environment-info-link')}
</a>}
{'.'}
</Trans>
</span>
}
</Row>
{/* {!environmentName
? <></>
: <Row id='qt-environment'>
<span className='header-font my-auto mx-1'>
{environmentName}
</span>
</Row>
} */}
<Navbar id='user-container' >
<NavDropdown
className="nav-dropdown-title"
title={userName}
id="responsive-navbar-nav"
>
<Nav.Link
className='mx-0 dropdown-item'
onClick={handleLogout}
>
{t('translation:logout')}
</Nav.Link>
<NavDropdown.Divider className='m-0' />
<Nav.Link className='mx-0 dropdown-item' href={changePasswordUrl} target='passwordchange'>
{t('translation:change-password')}
</Nav.Link>
</NavDropdown>
</Navbar>
</Container >
)
}
Example #13
Source File: record-patient-data.component.tsx From cwa-quick-test-frontend with Apache License 2.0 | 4 votes |
RecordPatientData = (props: any) => {
const context = React.useContext(AppContext);
const { t } = useTranslation();
const { keycloak } = useKeycloak();
const [isInit, setIsInit] = React.useState(false)
const [uuIdHash, setUuIdHash] = React.useState('');
const [isBack, setIsBack] = React.useState(true);
const isBackRef = React.useRef(isBack);
isBackRef.current = isBack;
const [processId, setProcessId] = React.useState('');
const processIdRef = React.useRef(processId);
processIdRef.current = processId;
const [person, setPerson] = React.useState<IPersonData>();
const [address, setAddress] = React.useState<IAddressData>();
const [pcrEnabled, setPcrEnabled] = React.useState(false);
const [testType, setTestType] = React.useState(TestType.RAT);
const [phoneNumber, setPhoneNumber] = React.useState('');
const [emailAddress, setEmailAddress] = React.useState('');
const [additionalInfo, setAdditionalInfo] = React.useState('');
const [consent, setConsent] = React.useState(false);
const [dccConsent, setDccConsent] = React.useState(false);
const [dccNoConsent, setDccNoConsent] = React.useState(false);
const [persDataInQR, setIncludePersData] = React.useState(false)
const [privacyAgreement, setPrivacyAgreement] = React.useState(false)
const [validated, setValidated] = React.useState(false);
const handleError = (error: any) => {
let msg = '';
if (error) {
msg = error.message
}
if (error && error.message && (error.message as string).includes('412')) {
msg = t('translation:no-group-error');
}
props.setError({ error: error, message: msg, onCancel: context.navigation!.toLanding });
}
const handleDelete = () => { deleteQuicktest(processIdRef.current); }
const [uuid, deleteQuicktest] = useGetUuid(props?.quickTest?.uuId, undefined, handleError);
useOnUnload(() => handleDelete);
React.useEffect(() => {
return () => {
if (isBackRef.current) {
handleDelete();
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
React.useEffect(() => {
if (keycloak.idTokenParsed) {
setPcrEnabled(!!(keycloak.idTokenParsed as any).pcr_enabled);
}
}, [keycloak])
// set values from props or new uuid on mount
React.useEffect(() => {
if (props.quickTest) {
const p = props.quickTest;
setConsent(p.processingConsens);
setIncludePersData(p.includePersData);
setPrivacyAgreement(p.privacyAgreement);
setPhoneNumber(p.phoneNumber);
if (p.emailAddress) {
setEmailAddress(p.emailAddress);
}
if (p.additionalInfo) {
setAdditionalInfo(p.additionalInfo);
}
setDccConsent(p.dccConsent);
setDccNoConsent(!p.dccConsent);
setTestType(p.testType);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.quickTest]);
// set hash from uuid
React.useEffect(() => {
if (uuid) {
setUuIdHash(sha256(uuid).toString());
}
}, [uuid]);
// set process id from hash
React.useEffect(() => {
setProcessId(utils.shortHash(uuIdHash));
}, [uuIdHash]);
// set ready state for spinner
React.useEffect(() => {
if (processId && context.navigation && context.valueSets) {
setIsInit(true);
}
}, [processId, context.navigation, context.valueSets])
const handleDccConsentChange = (evt: any) => {
setDccConsent(true)
setDccNoConsent(false);
}
const handleDccNoConsentChange = (evt: any) => {
setDccConsent(false)
setDccNoConsent(true);
}
const handleConsentChange = (evt: any) => {
setConsent(!consent)
setIncludePersData(false);
}
const handlePersDataInQRChange = (evt: any) => {
setIncludePersData(!persDataInQR);
setConsent(false);
}
const handleCancel = () => {
props.setQuickTest(undefined);
context.navigation!.toLanding();
}
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
const form = event.currentTarget;
event.preventDefault();
event.stopPropagation();
setValidated(true);
setIsBack(false);
if (form.checkValidity() && person) {
props.setQuickTest({
personData: person,
addressData: address,
processingConsens: consent,
uuId: uuid,
includePersData: persDataInQR,
privacyAgreement: privacyAgreement,
phoneNumber: phoneNumber,
emailAddress: emailAddress || undefined,
dccConsent: dccConsent,
additionalInfo: additionalInfo || undefined,
testType: testType
})
setTimeout(context.navigation!.toShowRecordPatient, 200);
}
}
return (
!(isInit && context && context.valueSets)
? <CwaSpinner />
: <Fade appear={true} in={true} >
<Container className='form-flex p-0 '>
<Row id='process-row'>
<span className='font-weight-bold mr-2'>{t('translation:process')}</span>
<span>{processId}</span>
</Row>
<Card id='data-card'>
<Form className='form-flex' onSubmit={handleSubmit} validated={validated}>
{/*
header with title and id card query
*/}
<CardHeader idCard={true} title={t('translation:record-result2')} />
{/*
content area with patient inputs and check box
*/}
<Card.Body id='data-body' className='pt-0'>
{/* dccConsent */}
<Row className='yellow'>
<Col className='p-0' xs='5' sm='3'>
<Row className='m-0 mb-2'>
<Col className='p-0' xs='auto'>
<Form.Label className='input-label pl-1'>
{t('translation:testZertifikat')}*
</Form.Label>
</Col>
<Col className='p-0 jcc-xs-jcfs-lg ml-lg-2 d-flex'>
<Image className="eu-flag" src={eu_logo} />
</Col>
</Row>
</Col>
<Col xs='7' sm='9'>
<Row className='m-0'>
<Form.Label className='input-label m-0'>{t('translation:dccConsent')}</Form.Label>
</Row>
<Collapse in={dccConsent}>
<Row className='m-0 my-1'>
<Form.Label className='input-label text-justify m-0'>
<Form.Label className='input-label mb-0 mr-2'>

</Form.Label>
{t('translation:dccConsent-cwa-only')}
</Form.Label>
</Row>
</Collapse>
<Row className='m-0 mb-2'>
<FormGroupDccConsentRadio controlId='dccConsent-radio1' name="dccConsent-radios" title={t('translation:ja')}
checked={dccConsent}
onChange={handleDccConsentChange}
required={true}
/>
<FormGroupDccConsentRadio controlId='dccConsent-radio2' name="dccConsent-radios" title={t('translation:nein')}
checked={dccNoConsent}
onChange={handleDccNoConsentChange}
required={true}
/>
</Row>
</Col>
</Row>
<hr />
{!pcrEnabled
? <></>
: <>
<Row>
<Form.Label className='input-label txt-no-wrap' column xs='5' sm='3'>{t('translation:test-type') + '*'}</Form.Label>
<Col xs='7' sm='9' className='d-flex'>
<Row>
<FormGroupInlineRadio controlId='test-type1' name="test-type-radios" title={t(`translation:${TestType.RAT}`)} sm='6'
checked={testType === TestType.RAT}
onChange={() => setTestType(TestType.RAT)}
/>
<FormGroupInlineRadio controlId='test-type2' name="test-type-radios" title={t(`translation:${TestType.PCR}`)} sm='6'
checked={testType === TestType.PCR}
onChange={() => setTestType(TestType.PCR)}
required={true}
/>
</Row>
</Col>
</Row>
<hr />
</>
}
<PersonInputs quickTest={props.quickTest} onChange={setPerson} dccConsent={dccConsent} onDccChanged={setDccConsent} />
<hr />
{/* address input */}
<AddressInputs quickTest={props.quickTest} onChange={setAddress} />
<hr />
{/* phone number input */}
< FormGroupInput controlId='formPhoneInput' title={t('translation:phone-number')}
value={phoneNumber}
onChange={(evt: any) => setPhoneNumber(evt.target.value)}
type='tel'
required
pattern={utils.pattern.tel}
maxLength={79}
/>
< FormGroupInput controlId='formEmailInput' title={t('translation:email-address')}
value={emailAddress}
onChange={(evt: any) => setEmailAddress(evt.target.value)}
type='email'
pattern={utils.pattern.eMail}
minLength={5}
maxLength={255}
/>
< FormGroupInput controlId='formAdditionalInfo' title={t('translation:additional-info')}
value={additionalInfo}
onChange={(evt: any) => setAdditionalInfo(evt.target.value)}
type='text'
minLength={1}
maxLength={250}
prepend='i'
tooltip={t('translation:additional-info-tooltip')}
/>
<hr />
{/* processing consent check box */}
<FormGroupConsentCkb controlId='formConsentCheckbox' title={t('translation:processing-consent-title')}
accordion={t('translation:processing-consent')}
onClick={handleConsentChange}
onChange={handleConsentChange}
type='radio'
name="check-radios"
checked={consent}
required={dccConsent}
/>
<FormGroupConsentCkb controlId='formKeepPrivateCheckbox' title={t('translation:patientdata-exclude-title')}
accordion={t('translation:patientdata-exclude')}
onClick={handlePersDataInQRChange}
onChange={handlePersDataInQRChange}
type='radio'
name="check-radios"
checked={persDataInQR}
required={dccConsent}
/>
<FormGroupConsentCkb controlId='formDataPrivacyCheckbox' title={t('translation:data-privacy-approve')}
onChange={(evt: any) => setPrivacyAgreement(evt.currentTarget.checked)}
type='checkbox'
checked={privacyAgreement}
required
/>
</Card.Body>
{/*
footer with clear and nex button
*/}
<CardFooter handleCancel={handleCancel} />
</Form>
</Card>
</Container>
</Fade>
)
}
Example #14
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>
</>
);
}