preact#FunctionalComponent TypeScript Examples
The following examples show how to use
preact#FunctionalComponent.
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: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
Header: FunctionalComponent<Props> = ({ user }) => {
if (!user) return <header />;
return (
<header class="main-header">
<img
width="40"
height="40"
alt={user.name}
src={`${user.picture}=s${40}-c`}
srcset={`${user.picture}=s${80}-c 2x`}
tabIndex={0}
class="user-image"
/>
<form method="post" action="/auth/logout">
<button class="unbutton log-out-button">Logout</button>
</form>
</header>
);
}
Example #2
Source File: index.tsx From netless-app with MIT License | 6 votes |
TimeCell: FunctionalComponent<TimeCellProps> = memo(
({ digit, disabled, onUp, onDown }) => {
const [oldDigit, setOldDigit] = useState(0);
const [flipped, setFlipped] = useState(false);
useEffect(() => {
if (disabled) {
setFlipped(true);
const timeout = window.setTimeout(() => {
setFlipped(false);
setOldDigit(digit);
}, 500);
return () => window.clearTimeout(timeout);
} else {
setOldDigit(digit);
}
}, [digit]);
return (
<div class={classNames("time-cell", { disabled })}>
<div class="time-cell-up" onClick={disabled ? void 0 : onUp} />
<div class="rotor">
<div class={classNames("rotor-leaf", { flipped })}>
<figure class="rotor-leaf-rear">{digit}</figure>
<figure class="rotor-leaf-front">{oldDigit}</figure>
</div>
<div class="rotor-top">{digit}</div>
<div class="rotor-bottom">{oldDigit}</div>
</div>
<div class="time-cell-down" onClick={disabled ? void 0 : onDown} />
</div>
);
}
)
Example #3
Source File: index.tsx From tic-tac-toe-app with MIT License | 6 votes |
Notfound: FunctionalComponent = () => {
return (
<div class={style.notfound}>
<h1>Error 404</h1>
<p>That page doesn't exist.</p>
<Link href="/"><h4>Back to Home</h4></Link>
</div>
);
}
Example #4
Source File: index.tsx From tic-tac-toe-app with MIT License | 6 votes |
Privacy: FunctionalComponent = () => {
return (
<Container>
<div class={style.privacy}>
We only collect anonymous user data (IP address, software and
hardware info) when the app crashes to make the user experience
better.
</div>
</Container>
);
}
Example #5
Source File: index.tsx From tic-tac-toe-app with MIT License | 6 votes |
Header: FunctionalComponent<PropTypes> = ({ title }) => {
return (
<header class={style.header}>
<h1>{title}</h1>
<nav>
<Link activeClassName={style.active} href="/">
Home
</Link>
<Link activeClassName={style.active} href="/privacy">
Privacy
</Link>
</nav>
</header>
);
}
Example #6
Source File: app.tsx From tic-tac-toe-app with MIT License | 6 votes |
App: FunctionalComponent = () => {
return (
<div id="app">
<Header title="Tic Tac Toe" />
<Router>
<Route path="/" component={Home} />
<Route path="/privacy/" component={Privacy} />
<NotFoundPage default />
</Router>
</div>
);
}
Example #7
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
LoggedOut: FunctionalComponent = () => {
return (
<UserPage>
<div class="log-in-container">
<form method="post" action="/auth/login">
<button
class="unbutton vote-button"
style={{
'--color-from': getHexColor(palette[5][0]),
'--color-to': getHexColor(palette[5][1]),
}}
>
Log in
</button>
</form>
</div>
</UserPage>
);
}
Example #8
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
LoggedIn: FunctionalComponent<Props> = ({ user, initialState }) => {
return (
<UserPage user={user}>
<script type="module" src={bundleURL} />
{imports.map(i => (
// @ts-ignore https://github.com/preactjs/preact/pull/2068
<link rel="preload" as="script" href={i} crossOrigin="" />
))}
<script
dangerouslySetInnerHTML={{
__html: `self.initialState = ${JSON.stringify(initialState)}`,
}}
></script>
<div class="vote-container" />
</UserPage>
);
}
Example #9
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
BigScreenIframePage: FunctionalComponent<{}> = () => {
return (
<html>
<head>
<title>{title}</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link
rel="icon"
href="https://cdn.glitch.com/b7996c5b-5a36-4f1b-84db-52a31d101dfc%2Ffavicon.png?v=1577974253219"
/>
<link rel="stylesheet" href={cssPath} />
<script type="module" src={bundleURL} />
{imports.map(i => (
// @ts-ignore https://github.com/preactjs/preact/pull/2068
<link rel="preload" as="script" href={i} crossOrigin="" />
))}
</head>
<body>
<div class="big-screen-container"></div>
</body>
</html>
);
}
Example #10
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
BigScreenPage: FunctionalComponent<{}> = () => {
return (
<html>
<head>
<title>{title}</title>
<meta
name="viewport"
content="width=device-width,initial-scale=1,user-scalable=no"
/>
<link
rel="icon"
href="https://cdn.glitch.com/b7996c5b-5a36-4f1b-84db-52a31d101dfc%2Ffavicon.png?v=1577974253219"
/>
<link rel="stylesheet" href={cssPath} />
<script type="module" src={bundleURL} />
{imports.map(i => (
// @ts-ignore https://github.com/preactjs/preact/pull/2068
<link rel="preload" as="script" href={i} crossOrigin="" />
))}
</head>
<body>
<iframe src="/big-screen/iframe/" />
</body>
</html>
);
}
Example #11
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
TopicsAdminPage: FunctionalComponent = () => {
return (
<html>
<head>
<title>Topics - Admin - {title}</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="stylesheet" href={cssPath} />
<script type="module" src={bundleURL} />
</head>
<body>
<h1>Topics</h1>
<p>
<a href="/admin/">Back</a>
</p>
<div class="topics-container" />
</body>
</html>
);
}
Example #12
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
AdminSimpleLogin: FunctionalComponent = () => {
return (
<html>
<head>
<title>Admin - {title}</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="stylesheet" href={cssPath} />
</head>
<body>
<h1>Login</h1>
<form action="/auth/admin-login" method="POST" class="admin-form-items">
<div class="admin-form-item">
<div>
<label>
<span class="label">Password</span>
<input
class="input"
type="password"
name="password"
autoFocus
/>
</label>
</div>
<div>
<button class="button">Log in</button>
</div>
</div>
</form>
</body>
</html>
);
}
Example #13
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
AdminPage: FunctionalComponent = () => {
return (
<html>
<head>
<title>Admin - {title}</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link
rel="icon"
href="https://cdn.glitch.com/b7996c5b-5a36-4f1b-84db-52a31d101dfc%2Ffavicon.png?v=1577974253219"
/>
<link rel="stylesheet" href={cssPath} />
<script type="module" src={bundleURL} />
{imports.map(i => (
// @ts-ignore https://github.com/preactjs/preact/pull/2068
<link rel="preload" as="script" href={i} crossOrigin="" />
))}
</head>
<body>
<h1>Admin</h1>
<p>
<a href="/admin/topics/">Edit topics</a>
</p>
<div class="admin-container" />
</body>
</html>
);
}
Example #14
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
Select: FunctionalComponent<JSX.HTMLAttributes> = props => {
const { class: className, ...selectProps } = props;
return (
<div class={'select' + (className ? ' ' + className : '')}>
<select {...selectProps}></select>
<div class="select-icon">
<svg class="fill-current h-4 w-4" viewBox="0 0 20 20">
<path d="M9.3 13l.7.7L15.7 8l-1.5-1.4-4.2 4.2-4.2-4.2L4.3 8z" />
</svg>
</div>
</div>
);
}
Example #15
Source File: vs.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
VS: FunctionalComponent<Props> = ({ colorFrom, colorTo, children, }: RenderableProps<Props>) => ( <div class="vs-circle" style={{ '--color-from': colorFrom || '', '--color-to': colorTo || '', }} > <div class="vs-outer vs-outer-1"></div> <div class="vs-outer vs-outer-2"></div> <div class="vs-outer vs-outer-3"></div> <div class="vs-outer vs-outer-4"></div> <div class="vs-innermost-circle">{children || 'VS'}</div> </div> )
Example #16
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
AdminVoteWrapper: FunctionalComponent<WrapperProps> = ({ children, onNewVoteClick, }) => ( <section> <h1>Active vote</h1> {children} <p> <button class="button" onClick={onNewVoteClick}> New vote </button> </p> </section> )
Example #17
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
TopicsSelect: FunctionalComponent<{
value: string;
topics: Topics;
onInput: (event: Event) => void;
}> = ({ onInput, topics, value }) => {
const sortedTopics = Object.entries(topics).sort((a, b) =>
a[1].label > b[1].label ? 1 : -1,
);
return (
<Select onInput={onInput} value={value}>
<option value=""></option>
{sortedTopics.map(([id, topic]) => (
<option value={id}>{topic.label}</option>
))}
</Select>
);
}
Example #18
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 6 votes |
AdminBracketWrapper: FunctionalComponent<innerProps> = ({ children, onGenerateClick, onZoomOut, }) => ( <section> <h1>Bracket</h1> {children} <div class="reset-row"> <button class="button button-danger" onClick={onGenerateClick}> Regenerate bracket </button>{' '} <button class="button" onClick={onZoomOut}> Zoom out </button> </div> </section> )
Example #19
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 5 votes |
UserPage: FunctionalComponent<Props> = ({ children, user }) => {
return (
<html>
<head>
<title>{title}</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link
rel="preload"
as="font"
crossOrigin=""
href="https://cdn.glitch.com/b7996c5b-5a36-4f1b-84db-52a31d101dfc%2Fnormal.woff2?v=1577974248786"
/>
<link
rel="preload"
as="font"
crossOrigin=""
href="https://cdn.glitch.com/b7996c5b-5a36-4f1b-84db-52a31d101dfc%2Fbold.woff2?v=1577974248594"
/>
<link
rel="icon"
href="https://cdn.glitch.com/b7996c5b-5a36-4f1b-84db-52a31d101dfc%2Ffavicon.png?v=1577974253219"
/>
<style
dangerouslySetInnerHTML={{
__html: inlineCSS,
}}
></style>
</head>
<body>
<div class="main-ui">
<Header user={user} />
<div class="main-content-container">
<div class="main-content">
<div class="site-title">
<h1>{title}</h1>
<p>{subTitle}</p>
</div>
<div class="main-action-area">{children}</div>
</div>
</div>
</div>
</body>
</html>
);
}
Example #20
Source File: index.tsx From tic-tac-toe-app with MIT License | 5 votes |
Container: FunctionalComponent = ({ children }) => {
return <div class={style.container}>{children}</div>;
}
Example #21
Source File: index.tsx From big-web-quiz with Apache License 2.0 | 5 votes |
AdminSelectedBracketWrapper: FunctionalComponent<{}> = ({ children }) => (
<section>
<h1>Selected bracket</h1>
{children}
</section>
)
Example #22
Source File: index.tsx From netless-app with MIT License | 5 votes |
Clock: FunctionalComponent<ClockProps> = memo(
({ minutes, seconds, disabled, onAdjustTime }) => {
const mins = useDigits(minutes);
const secs = useDigits(seconds);
const addTenMinutes = useAdjustTime(onAdjustTime, TimeAdjustment.AddTenMinutes);
const reduceTenMinutes = useAdjustTime(onAdjustTime, TimeAdjustment.ReduceTenMinutes);
const addOneMinute = useAdjustTime(onAdjustTime, TimeAdjustment.AddOneMinute);
const reduceOneMinute = useAdjustTime(onAdjustTime, TimeAdjustment.ReduceOneMinute);
const addTenSeconds = useAdjustTime(onAdjustTime, TimeAdjustment.AddTenSeconds);
const reduceTenSeconds = useAdjustTime(onAdjustTime, TimeAdjustment.ReduceTenSeconds);
const addOneSecond = useAdjustTime(onAdjustTime, TimeAdjustment.AddOneSecond);
const reduceOneSecond = useAdjustTime(onAdjustTime, TimeAdjustment.ReduceOneSecond);
return (
<div className="countdown-clock">
<TimeCell
disabled={disabled}
digit={mins[0]}
onUp={addTenMinutes}
onDown={reduceTenMinutes}
/>
<TimeCell
digit={mins[1]}
disabled={disabled}
onUp={addOneMinute}
onDown={reduceOneMinute}
/>
<div class="countdown-clock-divider" />
<TimeCell
disabled={disabled}
digit={secs[0]}
onUp={addTenSeconds}
onDown={reduceTenSeconds}
/>
<TimeCell
digit={secs[1]}
disabled={disabled}
onUp={addOneSecond}
onDown={reduceOneSecond}
/>
</div>
);
}
)
Example #23
Source File: index.tsx From netless-app with MIT License | 5 votes |
Countdown: FunctionalComponent<CountdownProps> = memo(
({
readonly,
countdownSecs,
startTime,
paused,
onAdjustTime,
onStart,
onPause,
onResume,
onReset,
}) => {
const [now, setNow] = useState(0);
const started = startTime > 0 && now >= startTime;
const amountInSecs = countdownSecs - (started ? now - startTime : 0);
const minutes = Math.floor(amountInSecs / 60);
const seconds = amountInSecs - minutes * 60;
useEffect(() => {
let timeout = NaN;
if (countdownSecs > 0 && startTime > 0 && !paused) {
const updateNow = () => {
setNow(Math.floor(Date.now() / 1000));
timeout = window.requestAnimationFrame(updateNow);
};
updateNow();
return () => window.cancelAnimationFrame(timeout);
} else {
window.cancelAnimationFrame(timeout);
}
}, [startTime, paused, countdownSecs]);
useEffect(() => {
if (startTime > 0 && now - startTime >= countdownSecs) {
onReset();
}
}, [now, startTime, countdownSecs, onReset]);
return (
<div class="netless-app-countdown">
<div class="netless-app-countdown-shrink">
<Clock
minutes={minutes}
seconds={seconds}
disabled={readonly || started}
onAdjustTime={onAdjustTime}
/>
<div class="netless-app-countdown-btns">
{paused ? (
<>
<button onClick={onReset} disabled={readonly}>
Reset
</button>
<button onClick={onResume} disabled={readonly}>
Resume
</button>
</>
) : started ? (
<button onClick={onPause} disabled={readonly}>
Pause
</button>
) : (
<button onClick={onStart} disabled={readonly || amountInSecs <= 0}>
Start
</button>
)}
</div>
</div>
</div>
);
}
)
Example #24
Source File: CardInput.tsx From adyen-web with MIT License | 4 votes |
CardInput: FunctionalComponent<CardInputProps> = props => {
const sfp = useRef(null);
const billingAddressRef = useRef(null);
const isValidating = useRef(false);
const cardInputRef = useRef<CardInputRef>({});
// Just call once
if (!Object.keys(cardInputRef.current).length) {
props.setComponentRef(cardInputRef.current);
}
const hasPanLengthRef = useRef(0);
const isAutoJumping = useRef(false);
const errorFieldId = 'creditCardErrors';
const { collateErrors, moveFocus, showPanel } = props.SRConfig;
const specifications = useMemo(() => new Specifications(props.specifications), [props.specifications]);
// Creates access to sfp so we can call functionality on it (like handleOnAutoComplete) directly from the console. Used for testing.
if (process.env.NODE_ENV === 'development') cardInputRef.current.sfp = sfp;
/**
* STATE HOOKS
*/
const [status, setStatus] = useState('ready');
const [errors, setErrors] = useState<CardInputErrorState>({});
const [valid, setValid] = useState<CardInputValidState>({
...(props.holderNameRequired && { holderName: false })
});
const [data, setData] = useState<CardInputDataState>({
...(props.hasHolderName && { holderName: props.data.holderName ?? '' })
});
// An object containing a collection of all the errors that can be passed to the ErrorPanel to be read by the screenreader
const [mergedSRErrors, setMergedSRErrors] = useState<ErrorPanelObj>(null);
const [focusedElement, setFocusedElement] = useState('');
const [isSfpValid, setIsSfpValid] = useState(false);
const [expiryDatePolicy, setExpiryDatePolicy] = useState(DATE_POLICY_REQUIRED);
const [cvcPolicy, setCvcPolicy] = useState(CVC_POLICY_REQUIRED);
const [issuingCountryCode, setIssuingCountryCode] = useState<string>(null);
const [dualBrandSelectElements, setDualBrandSelectElements] = useState([]);
const [selectedBrandValue, setSelectedBrandValue] = useState('');
const showBillingAddress = props.billingAddressMode !== AddressModeOptions.none && props.billingAddressRequired;
const partialAddressSchema = handlePartialAddressMode(props.billingAddressMode);
const [storePaymentMethod, setStorePaymentMethod] = useState(false);
const [billingAddress, setBillingAddress] = useState<AddressData>(showBillingAddress ? props.data.billingAddress : null);
const [showSocialSecurityNumber, setShowSocialSecurityNumber] = useState(false);
const [socialSecurityNumber, setSocialSecurityNumber] = useState('');
const [installments, setInstallments] = useState<InstallmentsObj>({ value: null });
// re. Disable arrows for iOS: The name of the element calling for other elements to be disabled
// - either a securedField type (like 'encryptedCardNumber') when call is coming from SF
// or else the name of an internal, Adyen-web, element (like 'holderName')
const [iOSFocusedField, setIOSFocusedField] = useState(null);
/**
* LOCAL VARS
*/
const {
handleChangeFor,
triggerValidation,
data: formData,
valid: formValid,
errors: formErrors,
setSchema,
setData: setFormData,
setValid: setFormValid,
setErrors: setFormErrors
} = useForm<CardInputDataState>({
schema: [],
defaultData: props.data,
formatters: cardInputFormatters,
rules: cardInputValidationRules
});
const hasInstallments = !!Object.keys(props.installmentOptions).length;
const showAmountsInInstallments = props.showInstallmentAmounts ?? true;
const cardCountryCode: string = issuingCountryCode ?? props.countryCode;
const isKorea = cardCountryCode === 'kr'; // If issuingCountryCode or the merchant defined countryCode is set to 'kr'
const showKCP = props.configuration.koreanAuthenticationRequired && isKorea;
const showBrazilianSSN: boolean =
(showSocialSecurityNumber && props.configuration.socialSecurityNumberMode === 'auto') ||
props.configuration.socialSecurityNumberMode === 'show';
/**
* HANDLERS
*/
// SecuredField-only handler
const handleFocus = getFocusHandler(setFocusedElement, props.onFocus, props.onBlur);
const retrieveLayout = () => {
return getLayout({
props,
showKCP,
showBrazilianSSN,
...(props.billingAddressRequired && {
countrySpecificSchemas: specifications.getAddressSchemaForCountry(billingAddress?.country),
billingAddressRequiredFields: props.billingAddressRequiredFields
})
});
};
/**
* re. Disabling arrow keys in iOS:
* Only by disabling all fields in the Card PM except for the active securedField input can we force the iOS soft keyboard arrow keys to disable
*
* @param obj - has fieldType prop saying whether this function is being called in response to an securedFields click ('encryptedCardNumber' etc)
* - in which case we should disable all non-SF fields
* or,
* due to an internal action ('webInternalElement') - in which case we can enable all non-SF fields
*/
const handleTouchstartIOS = useCallback((obj: TouchStartEventObj) => {
const elementType = obj.fieldType !== 'webInternalElement' ? obj.fieldType : obj.name;
setIOSFocusedField(elementType);
}, []);
// Callback for ErrorPanel
const handleErrorPanelFocus = getErrorPanelHandler(isValidating, sfp, handleFocus);
const handleAddress = getAddressHandler(setFormData, setFormValid, setFormErrors);
const doPanAutoJump = getAutoJumpHandler(isAutoJumping, sfp, retrieveLayout());
const handleSecuredFieldsChange = (sfState: SFPState, eventDetails?: OnChangeEventDetails): void => {
// Clear errors so that the screenreader will read them *all* again - without this it only reads the newly added ones
setMergedSRErrors(null);
/**
* Handling auto complete value for holderName (but only if the component is using a holderName field)
*/
if (sfState.autoCompleteName) {
if (!props.hasHolderName) return;
const holderNameValidationFn = getRuleByNameAndMode('holderName', 'blur');
const acHolderName = holderNameValidationFn(sfState.autoCompleteName) ? sfState.autoCompleteName : null;
if (acHolderName) {
setFormData('holderName', acHolderName);
setFormValid('holderName', true); // only if holderName is valid does this fny get called - so we know it's valid and w/o error
setFormErrors('holderName', null);
}
return;
}
/**
* If PAN has just become valid: decide if we can shift focus to the next field.
*
* We can if the config prop, autoFocus, is true AND we have a panLength value from binLookup AND one of the following scenarios is true:
* - If encryptedCardNumber was invalid but now is valid
* [scenario: shopper has typed in a number and field is now valid]
* - If encryptedCardNumber was valid and still is valid and we're handling an onBrand event (triggered by binLookup which has happened after the handleOnFieldValid event)
* [scenario: shopper has pasted in a full, valid, number]
*/
if (
props.autoFocus &&
hasPanLengthRef.current > 0 &&
((!valid.encryptedCardNumber && sfState.valid?.encryptedCardNumber) ||
(valid.encryptedCardNumber && sfState.valid.encryptedCardNumber && eventDetails.event === 'handleOnBrand'))
) {
doPanAutoJump();
}
/**
* Process SFP state
*/
setData({ ...data, ...sfState.data });
setErrors({ ...errors, ...sfState.errors });
setValid({ ...valid, ...sfState.valid });
setIsSfpValid(sfState.isSfpValid);
// Values relating to /binLookup response
setCvcPolicy(sfState.cvcPolicy);
setShowSocialSecurityNumber(sfState.showSocialSecurityNumber);
setExpiryDatePolicy(sfState.expiryDatePolicy);
};
// Farm the handlers for binLookup related functionality out to another 'extensions' file
const extensions = useMemo(
() =>
CIExtensions(
props,
{ sfp },
{ dualBrandSelectElements, setDualBrandSelectElements, setSelectedBrandValue, issuingCountryCode, setIssuingCountryCode },
hasPanLengthRef
),
[dualBrandSelectElements, issuingCountryCode]
);
/**
* EXPOSE METHODS expected by Card.tsx
*/
cardInputRef.current.showValidation = () => {
// Clear errors so that the screenreader will read them *all* again
setMergedSRErrors(null);
// Validate SecuredFields
sfp.current.showValidation();
// Validate holderName & SSN & KCP (taxNumber) but *not* billingAddress
triggerValidation(['holderName', 'socialSecurityNumber', 'taxNumber']);
// Validate Address
if (billingAddressRef?.current) billingAddressRef.current.showValidation();
isValidating.current = true;
};
cardInputRef.current.processBinLookupResponse = (binLookupResponse: BinLookupResponse, isReset: boolean) => {
extensions.processBinLookup(binLookupResponse, isReset);
};
cardInputRef.current.setStatus = setStatus;
/**
* EFFECT HOOKS
*/
useEffect(() => {
// componentDidMount - expose more methods expected by Card.tsx
cardInputRef.current.setFocusOn = sfp.current.setFocusOn;
cardInputRef.current.updateStyles = sfp.current.updateStyles;
cardInputRef.current.handleUnsupportedCard = sfp.current.handleUnsupportedCard;
// componentWillUnmount
return () => {
sfp.current.destroy();
};
}, []);
/**
* Handle form schema updates
*/
useEffect(() => {
const newSchema = [
...(props.hasHolderName ? ['holderName'] : []),
...(showBrazilianSSN ? ['socialSecurityNumber'] : []),
...(showKCP ? ['taxNumber'] : []),
...(showBillingAddress ? ['billingAddress'] : [])
];
setSchema(newSchema);
}, [props.hasHolderName, showBrazilianSSN, showKCP]);
/**
* Handle updates from useForm
*/
useEffect(() => {
// Clear errors so that the screenreader will read them *all* again
setMergedSRErrors(null);
setData({ ...data, holderName: formData.holderName ?? '', taxNumber: formData.taxNumber });
setSocialSecurityNumber(formData.socialSecurityNumber);
if (showBillingAddress) setBillingAddress({ ...formData.billingAddress });
setValid({
...valid,
holderName: props.holderNameRequired ? formValid.holderName : true,
// Setting value to false if it's falsy keeps in line with existing, expected behaviour
// - but there is an argument to allow 'undefined' as a value to indicate the non-presence of the field
socialSecurityNumber: formValid.socialSecurityNumber ? formValid.socialSecurityNumber : false,
taxNumber: formValid.taxNumber ? formValid.taxNumber : false,
billingAddress: formValid.billingAddress ? formValid.billingAddress : false
});
// Check if billingAddress errors object has any properties that aren't null or undefined
const addressHasErrors = formErrors.billingAddress
? Object.entries(formErrors.billingAddress).reduce((acc, [, error]) => acc || error != null, false)
: false;
// Errors
setErrors({
...errors,
holderName: props.holderNameRequired && !!formErrors.holderName ? formErrors.holderName : null,
socialSecurityNumber: showBrazilianSSN && !!formErrors.socialSecurityNumber ? formErrors.socialSecurityNumber : null,
taxNumber: showKCP && !!formErrors.taxNumber ? formErrors.taxNumber : null,
billingAddress: showBillingAddress && addressHasErrors ? formErrors.billingAddress : null
});
}, [formData, formValid, formErrors]);
/**
* Main 'componentDidUpdate' handler
*/
useEffect(() => {
const holderNameValid: boolean = valid.holderName;
const sfpValid: boolean = isSfpValid;
const addressValid: boolean = showBillingAddress ? valid.billingAddress : true;
const koreanAuthentication: boolean = showKCP ? !!valid.taxNumber && !!valid.encryptedPassword : true;
const socialSecurityNumberValid: boolean = showBrazilianSSN ? !!valid.socialSecurityNumber : true;
const isValid: boolean = sfpValid && holderNameValid && addressValid && koreanAuthentication && socialSecurityNumberValid;
const sfStateErrorsObj = sfp.current.mapErrorsToValidationRuleResult();
const mergedErrors = { ...errors, ...sfStateErrorsObj }; // maps sfErrors AND solves race condition problems for sfp from showValidation
// Extract and then flatten billingAddress errors into a new object with *all* the field errors at top level
const { billingAddress: extractedAddressErrors, ...errorsWithoutAddress } = mergedErrors;
const errorsForPanel = { ...errorsWithoutAddress, ...extractedAddressErrors };
const sortedMergedErrors = sortErrorsForPanel({
errors: errorsForPanel,
layout: retrieveLayout(),
i18n: props.i18n,
countrySpecificLabels: specifications.getAddressLabelsForCountry(billingAddress?.country)
});
setMergedSRErrors(sortedMergedErrors);
props.onChange({
data,
valid,
errors: mergedErrors,
isValid,
billingAddress,
selectedBrandValue,
storePaymentMethod,
socialSecurityNumber,
installments
});
}, [data, valid, errors, selectedBrandValue, storePaymentMethod, installments]);
/**
* RENDER
*/
const FieldToRender = props.storedPaymentMethodId ? StoredCardFieldsWrapper : CardFieldsWrapper;
return (
<Fragment>
<SecuredFieldsProvider
ref={sfp}
{...extractPropsForSFP(props)}
styles={{ ...defaultStyles, ...props.styles }}
koreanAuthenticationRequired={props.configuration.koreanAuthenticationRequired}
hasKoreanFields={!!(props.configuration.koreanAuthenticationRequired && props.countryCode === 'kr')}
onChange={handleSecuredFieldsChange}
onBrand={props.onBrand}
onFocus={handleFocus}
type={props.brand}
isCollatingErrors={collateErrors}
onTouchstartIOS={handleTouchstartIOS}
render={({ setRootNode, setFocusOn }, sfpState) => (
<div
ref={setRootNode}
className={`adyen-checkout__card-input ${styles['card-input__wrapper']} adyen-checkout__card-input--${props.fundingSource ??
'credit'}`}
role={collateErrors && 'form'}
aria-describedby={collateErrors ? errorFieldId : null}
>
<FieldToRender
// Extract exact props that we need to pass down
{...extractPropsForCardFields(props)}
// Pass on vars created in CardInput:
// Base (shared w. StoredCard)
data={data}
valid={valid}
errors={errors}
handleChangeFor={handleChangeFor}
focusedElement={focusedElement}
setFocusOn={setFocusOn}
sfpState={sfpState}
collateErrors={collateErrors}
errorFieldId={errorFieldId}
cvcPolicy={cvcPolicy}
hasInstallments={hasInstallments}
showAmountsInInstallments={showAmountsInInstallments}
handleInstallments={setInstallments}
// For Card
brandsIcons={props.brandsIcons}
mergedSRErrors={mergedSRErrors}
moveFocus={moveFocus}
showPanel={showPanel}
handleErrorPanelFocus={handleErrorPanelFocus}
formData={formData}
formErrors={formErrors}
formValid={formValid}
expiryDatePolicy={expiryDatePolicy}
dualBrandSelectElements={dualBrandSelectElements}
extensions={extensions}
selectedBrandValue={selectedBrandValue}
// For KCP
showKCP={showKCP}
// For SSN
showBrazilianSSN={showBrazilianSSN}
socialSecurityNumber={socialSecurityNumber}
// For Store details
handleOnStoreDetails={setStorePaymentMethod}
// For Address
billingAddress={billingAddress}
handleAddress={handleAddress}
billingAddressRef={billingAddressRef}
partialAddressSchema={partialAddressSchema}
//
iOSFocusedField={iOSFocusedField}
/>
</div>
)}
/>
{props.showPayButton &&
props.payButton({
status,
icon: getImage({ loadingContext: props.loadingContext, imageFolder: 'components/' })('lock')
})}
</Fragment>
);
}
Example #25
Source File: App.tsx From netless-app with MIT License | 4 votes |
App: FunctionalComponent<AppProps> = memo(({ context, storage }) => {
const [isWritable, setWritable] = useState(() => context.getIsWritable());
const [countdownSecs, setCountdownSecs] = useState(0);
const [startTime, setStartTime] = useState(0);
const [paused, setPaused] = useState(false);
const started = startTime > 0;
const onStart = useCallback(() => {
if (context.getIsWritable()) {
setPaused(false);
setStartTime(Math.floor(Date.now() / 1000));
}
}, [context]);
const onPause = useCallback(() => {
if (context.getIsWritable()) {
setPaused(true);
}
}, [context]);
const onResume = useCallback(() => {
if (context.getIsWritable()) {
setPaused(false);
}
}, [context]);
const onReset = useCallback(() => {
if (context.getIsWritable()) {
setPaused(false);
setCountdownSecs(0);
setStartTime(0);
}
}, [context]);
const onAdjustTime = useCallback(
(adjustment: number) => {
if (!started) {
setCountdownSecs(countdownSecs => {
if (!context.getIsWritable()) {
return countdownSecs;
}
const minutes = Math.floor(countdownSecs / 60);
const seconds = countdownSecs - minutes * 60;
const min1 = Math.floor(minutes / 10);
const min2 = minutes % 10;
const sec1 = Math.floor(seconds / 10);
const sec2 = seconds % 10;
switch (adjustment) {
case TimeAdjustment.AddTenMinutes: {
const max = min2 + seconds === 0 ? 7 : 6;
return (((min1 + 1) % max) * 10 + min2) * 60 + seconds;
}
case TimeAdjustment.ReduceTenMinutes: {
const max = min2 + seconds === 0 ? 7 : 6;
return (((min1 + max - 1) % max) * 10 + min2) * 60 + seconds;
}
case TimeAdjustment.AddOneMinute: {
return (Math.min(5, min1) * 10 + ((min2 + 1) % 10)) * 60 + seconds;
}
case TimeAdjustment.ReduceOneMinute: {
return (Math.min(5, min1) * 10 + ((min2 + 10 - 1) % 10)) * 60 + seconds;
}
case TimeAdjustment.AddTenSeconds: {
return minutes * 60 + (((sec1 + 1) % 6) * 10 + sec2);
}
case TimeAdjustment.ReduceTenSeconds: {
return minutes * 60 + (((sec1 + 6 - 1) % 6) * 10 + sec2);
}
case TimeAdjustment.AddOneSecond: {
return minutes * 60 + (Math.min(5, sec1) * 10 + ((sec2 + 1) % 10));
}
case TimeAdjustment.ReduceOneSecond: {
return minutes * 60 + (Math.min(5, sec1) * 10 + ((sec2 + 10 - 1) % 10));
}
default: {
return countdownSecs;
}
}
});
}
},
[started, context]
);
useEffect(() => {
setWritable(context.getIsWritable());
context.emitter.on("writableChange", setWritable);
return () => context.emitter.off("writableChange", setWritable);
}, [context]);
useEffect(() => {
const handler: StorageStateChangedListener<StorageState> = diff => {
if (diff.countdownSecs) {
setCountdownSecs(storage.state.countdownSecs);
}
if (diff.paused) {
setPaused(storage.state.paused);
}
if (diff.startTime) {
setStartTime(storage.state.startTime);
}
};
storage.onStateChanged.addListener(handler);
return () => storage.onStateChanged.removeListener(handler);
}, [storage]);
useEffect(() => {
if (context.getIsWritable()) {
// unchanged values will be skipped automatically
storage.setState({ countdownSecs, paused, startTime });
}
}, [countdownSecs, paused, startTime, context]);
return (
<Countdown
readonly={!isWritable}
countdownSecs={countdownSecs}
startTime={startTime}
paused={paused}
onAdjustTime={onAdjustTime}
onStart={onStart}
onPause={onPause}
onResume={onResume}
onReset={onReset}
/>
);
})
Example #26
Source File: Field.tsx From adyen-web with MIT License | 4 votes |
Field: FunctionalComponent<FieldProps> = props => {
//
const {
children,
className,
classNameModifiers,
dir,
disabled,
errorMessage,
helper,
inputWrapperModifiers,
isCollatingErrors,
isLoading,
isValid,
label,
name,
onBlur,
onFieldBlur,
onFocus,
onFocusField,
showValidIcon,
useLabelElement,
// Redeclare prop names to avoid internal clashes
filled: propsFilled,
focused: propsFocused
} = props;
const uniqueId = useRef(getUniqueId(`adyen-checkout-${name}`));
const [focused, setFocused] = useState(false);
const [filled, setFilled] = useState(false);
// The means by which focussed/filled is set for securedFields
if (propsFocused != null) setFocused(!!propsFocused);
if (propsFilled != null) setFilled(!!propsFilled);
// The means by which focussed/filled is set for other fields - this function is passed down to them and triggered
const onFocusHandler = useCallback(
(event: h.JSX.TargetedEvent<HTMLInputElement>) => {
setFocused(true);
onFocus?.(event);
},
[onFocus]
);
const onBlurHandler = useCallback(
(event: h.JSX.TargetedEvent<HTMLInputElement>) => {
setFocused(false);
onBlur?.(event);
// When we also need to fire a specific function when a field blurs
onFieldBlur?.(event);
},
[onBlur, onFieldBlur]
);
const renderContent = useCallback(() => {
return (
<Fragment>
{typeof label === 'string' && (
<span
className={classNames({
'adyen-checkout__label__text': true,
'adyen-checkout__label__text--error': errorMessage
})}
>
{label}
</span>
)}
{/*@ts-ignore - function is callable*/}
{typeof label === 'function' && label()}
{helper && <span className={'adyen-checkout__helper-text'}>{helper}</span>}
<div
className={classNames([
'adyen-checkout__input-wrapper',
...inputWrapperModifiers.map(m => `adyen-checkout__input-wrapper--${m}`)
])}
dir={dir}
>
{toChildArray(children).map(
(child: ComponentChild): ComponentChild => {
const childProps = {
isValid,
onFocusHandler,
onBlurHandler,
isInvalid: !!errorMessage,
...(name && { uniqueId: uniqueId.current })
};
return cloneElement(child as VNode, childProps);
}
)}
{isLoading && (
<span className="adyen-checkout-input__inline-validation adyen-checkout-input__inline-validation--loading">
<Spinner size="small" />
</span>
)}
{isValid && showValidIcon !== false && (
<span className="adyen-checkout-input__inline-validation adyen-checkout-input__inline-validation--valid">
<Icon type="checkmark" />
</span>
)}
{errorMessage && (
<span className="adyen-checkout-input__inline-validation adyen-checkout-input__inline-validation--invalid">
<Icon type="field_error" />
</span>
)}
</div>
{errorMessage && typeof errorMessage === 'string' && errorMessage.length && (
<span
className={'adyen-checkout__error-text'}
id={`${uniqueId.current}${ARIA_ERROR_SUFFIX}`}
aria-hidden={isCollatingErrors ? 'true' : null}
aria-live={isCollatingErrors ? null : 'polite'}
>
{errorMessage}
</span>
)}
</Fragment>
);
}, [children, errorMessage, isLoading, isValid, label, onFocusHandler, onBlurHandler]);
const LabelOrDiv = useCallback(({ onFocusField, focused, filled, disabled, name, uniqueId, useLabelElement, children }) => {
const defaultWrapperProps = {
onClick: onFocusField,
className: classNames({
'adyen-checkout__label': true,
'adyen-checkout__label--focused': focused,
'adyen-checkout__label--filled': filled,
'adyen-checkout__label--disabled': disabled
})
};
return useLabelElement ? (
<label {...defaultWrapperProps} htmlFor={name && uniqueId}>
{children}
</label>
) : (
<div {...defaultWrapperProps} role={'form'}>
{children}
</div>
);
}, []);
/**
* RENDER
*/
return (
<div
className={classNames(
'adyen-checkout__field',
className,
classNameModifiers.map(m => `adyen-checkout__field--${m}`),
{
'adyen-checkout__field--error': errorMessage,
'adyen-checkout__field--valid': isValid
}
)}
>
<LabelOrDiv
onFocusField={onFocusField}
name={name}
disabled={disabled}
filled={filled}
focused={focused}
useLabelElement={useLabelElement}
uniqueId={uniqueId.current}
>
{renderContent()}
</LabelOrDiv>
</div>
);
}