mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
🔀 Merge branch 'PCP-3981’
This commit is contained in:
commit
accf1b0619
12 changed files with 307 additions and 196 deletions
|
@ -24,6 +24,10 @@ $max-width-onboarding: 1024px;
|
|||
$max-width-onboarding-content: 500px;
|
||||
$max-width-settings: 938px;
|
||||
|
||||
:root {
|
||||
--ppcp-color-app-bg: #{$color-white};
|
||||
}
|
||||
|
||||
#ppcp-settings-container {
|
||||
--max-width-settings: #{$max-width-settings};
|
||||
--max-width-onboarding: #{$max-width-onboarding};
|
||||
|
|
|
@ -1,64 +1,111 @@
|
|||
.ppcp-r-navigation-container {
|
||||
padding: 24px 48px;
|
||||
position: sticky;
|
||||
top: var(--wp-admin--admin-bar--height);
|
||||
z-index: 10;
|
||||
|
||||
padding: 10px 48px;
|
||||
margin: 0 -20px 48px -20px;
|
||||
border-bottom: 1px solid $color-gray-300;
|
||||
position: relative;
|
||||
|
||||
box-shadow: 0 -1px 0 0 $color-gray-300 inset;
|
||||
background: var(--ppcp-color-app-bg);
|
||||
transition: box-shadow 0.3s;
|
||||
|
||||
--wp-components-color-accent: #{$color-blueberry};
|
||||
--color-text: #{$color-gray-900};
|
||||
--color-disabled: #CCC;
|
||||
|
||||
.ppcp-r-navigation {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
|
||||
button.is-primary {
|
||||
padding: 10px 18px;
|
||||
justify-content: center;
|
||||
margin: 0 0 0 12px;
|
||||
&:not(:disabled) {
|
||||
background-color: $color-blueberry;
|
||||
.components-button {
|
||||
@include font(13, 20, 400);
|
||||
|
||||
&.is-primary {
|
||||
background-color: var(--wp-components-color-accent);
|
||||
padding: 10px 16px;
|
||||
justify-content: center;
|
||||
margin: 0 0 0 12px;
|
||||
border-radius: 2px;
|
||||
|
||||
&:disabled {
|
||||
background-color: var(--color-disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.is-tertiary {
|
||||
@include font(16, 24, 600);
|
||||
color: $color-gray-900;
|
||||
&:hover{
|
||||
background-color:none;
|
||||
background:none;
|
||||
&.is-link {
|
||||
color: var(--wp-components-color-accent);
|
||||
text-decoration: none;
|
||||
|
||||
&:disabled {
|
||||
color: var(--color-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-title {
|
||||
@include font(16, 24, 600);
|
||||
color: var(--color-text);
|
||||
|
||||
.title {
|
||||
margin-left: 18px;
|
||||
}
|
||||
|
||||
.big {
|
||||
@include font(20, 28, 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--left {
|
||||
&__link {
|
||||
@include font(20, 28, 400);
|
||||
color: $color-gray-900;
|
||||
text-decoration: none;
|
||||
padding: 0 0 0 18px;
|
||||
}
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
&--right a{
|
||||
@include font(13, 20, 400);
|
||||
color: $color-blueberry;
|
||||
text-decoration: none;
|
||||
&--right {
|
||||
.is-link {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&--progress-bar {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: $color-blueberry;
|
||||
background-color: var(--wp-components-color-accent);
|
||||
height: 4px;
|
||||
transition: width 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
padding: 24px 35px;
|
||||
&.is-scrolled {
|
||||
box-shadow: 0 -1px 0 0 $color-gray-300 inset, 0 8px 8px 0 rgba(85, 93, 102, .3);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 782px) {
|
||||
padding: 10px 12px;
|
||||
|
||||
.ppcp-r-navigation {
|
||||
flex-wrap: wrap;
|
||||
row-gap: 8px;
|
||||
white-space: nowrap;
|
||||
|
||||
&--right {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
z-index: 10;
|
||||
background: var(--ppcp-color-app-bg);
|
||||
box-shadow: -5px 0 8px var(--ppcp-color-app-bg);
|
||||
}
|
||||
|
||||
&--progress-bar {
|
||||
display: none;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.components-button.is-title {
|
||||
.title {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
body:has(.ppcp-r-container--settings),
|
||||
body:has(.ppcp-r-container--onboarding) {
|
||||
background-color: var(--ppcp-color-app-bg) !important;
|
||||
|
||||
.woocommerce-layout,
|
||||
#woocommerce-layout__primary {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.notice,
|
||||
.nav-tab-wrapper.woo-nav-tab-wrapper,
|
||||
.woocommerce-layout__header,
|
||||
.wrap.woocommerce form > h2,
|
||||
#screen-meta-links {
|
||||
display: none !important;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
body:has(.ppcp-r-container--onboarding) {
|
||||
background-color: #fff !important;
|
||||
|
||||
.notice, .nav-tab-wrapper.woo-nav-tab-wrapper, .woocommerce-layout__header, .wrap.woocommerce form > h2, #screen-meta-links {
|
||||
display: none !important;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
body:has(.ppcp-r-container--settings) {
|
||||
background-color: #fff !important;
|
||||
|
||||
.notice, .nav-tab-wrapper.woo-nav-tab-wrapper, .woocommerce-layout, .wrap.woocommerce form > h2, #screen-meta-links {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
|
@ -28,5 +28,4 @@
|
|||
}
|
||||
|
||||
@import './components/reusable-components/payment-method-modal';
|
||||
@import './components/screens/onboarding-global';
|
||||
@import './components/screens/settings-global';
|
||||
@import './components/screens/fullscreen';
|
||||
|
|
|
@ -1,130 +1,73 @@
|
|||
import { Button } from '@wordpress/components';
|
||||
import { Button, Icon } from '@wordpress/components';
|
||||
import { chevronLeft } from '@wordpress/icons';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { OnboardingHooks } from '../../../../data';
|
||||
import data from '../../../../utils/data';
|
||||
import useIsScrolled from '../../../../hooks/useIsScrolled';
|
||||
|
||||
const Navigation = ( { setStep, setCompleted, currentStep, stepperOrder } ) => {
|
||||
const isLastStep = () => currentStep + 1 === stepperOrder.length;
|
||||
const isFistStep = () => currentStep === 0;
|
||||
const navigateBy = ( stepDirection ) => {
|
||||
let newStep = currentStep + stepDirection;
|
||||
const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => {
|
||||
const { title, isFirst, percentage, showNext, canProceed } = stepDetails;
|
||||
const { isScrolled } = useIsScrolled();
|
||||
|
||||
if ( isNaN( newStep ) || newStep < 0 ) {
|
||||
console.warn( 'Invalid next step:', newStep );
|
||||
newStep = 0;
|
||||
}
|
||||
|
||||
if ( newStep >= stepperOrder.length ) {
|
||||
setCompleted( true );
|
||||
} else {
|
||||
setStep( newStep );
|
||||
}
|
||||
};
|
||||
|
||||
const { products } = OnboardingHooks.useProducts();
|
||||
const { isCasualSeller } = OnboardingHooks.useBusiness();
|
||||
|
||||
let navigationTitle = '';
|
||||
let disabled = false;
|
||||
|
||||
switch ( currentStep ) {
|
||||
case 1:
|
||||
navigationTitle = __(
|
||||
'Set up store type',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
disabled = isCasualSeller === null;
|
||||
break;
|
||||
case 2:
|
||||
navigationTitle = __(
|
||||
'Select product types',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
disabled = products.length < 1;
|
||||
break;
|
||||
case 3:
|
||||
navigationTitle = __(
|
||||
'Choose checkout options',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
case 4:
|
||||
navigationTitle = __(
|
||||
'Connect your PayPal account',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
break;
|
||||
default:
|
||||
navigationTitle = __(
|
||||
'PayPal Payments',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
}
|
||||
const state = OnboardingHooks.useNavigationState();
|
||||
const isDisabled = ! canProceed( state );
|
||||
const className = classNames( 'ppcp-r-navigation-container', {
|
||||
'is-scrolled': isScrolled,
|
||||
} );
|
||||
|
||||
return (
|
||||
<div className="ppcp-r-navigation-container">
|
||||
<div className={ className }>
|
||||
<div className="ppcp-r-navigation">
|
||||
<div className="ppcp-r-navigation--left">
|
||||
<span>{ data().getImage( 'icon-arrow-left.svg' ) }</span>
|
||||
{ ! isFistStep() ? (
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={ () => navigateBy( -1 ) }
|
||||
>
|
||||
{ navigationTitle }
|
||||
</Button>
|
||||
) : (
|
||||
<a
|
||||
className="ppcp-r-navigation--left__link"
|
||||
href={ global.ppcpSettings.wcPaymentsTabUrl }
|
||||
aria-label={ __(
|
||||
'Return to payments',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
{ navigationTitle }
|
||||
</a>
|
||||
) }
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={ isFirst ? onExit : onPrev }
|
||||
className="is-title"
|
||||
>
|
||||
<Icon icon={ chevronLeft } />
|
||||
<span className={ 'title ' + ( isFirst ? 'big' : '' ) }>
|
||||
{ title }
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
{ ! isFistStep() && (
|
||||
<div className="ppcp-r-navigation--right">
|
||||
<a
|
||||
href={ global.ppcpSettings.wcPaymentsTabUrl }
|
||||
aria-label={ __(
|
||||
'Return to payments',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
{ __(
|
||||
'Save and exit',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</a>
|
||||
{ ! isLastStep() && (
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={ disabled }
|
||||
onClick={ () => navigateBy( 1 ) }
|
||||
>
|
||||
{ __(
|
||||
'Continue',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</Button>
|
||||
) }
|
||||
</div>
|
||||
) }
|
||||
<div
|
||||
className="ppcp-r-navigation--progress-bar"
|
||||
style={ {
|
||||
width: `${
|
||||
( currentStep / ( stepperOrder.length - 1 ) ) * 90
|
||||
}%`,
|
||||
} }
|
||||
></div>
|
||||
{ ! isFirst &&
|
||||
NextButton( { showNext, isDisabled, onNext, onExit } ) }
|
||||
<ProgressBar percent={ percentage } />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const NextButton = ( { showNext, isDisabled, onNext, onExit } ) => {
|
||||
return (
|
||||
<div className="ppcp-r-navigation--right">
|
||||
<Button variant="link" onClick={ onExit }>
|
||||
{ __( 'Save and exit', 'woocommerce-paypal-payments' ) }
|
||||
</Button>
|
||||
{ showNext && (
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={ isDisabled }
|
||||
onClick={ onNext }
|
||||
>
|
||||
{ __( 'Continue', 'woocommerce-paypal-payments' ) }
|
||||
</Button>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ProgressBar = ( { percent } ) => {
|
||||
percent = Math.min( Math.max( percent, 0 ), 100 );
|
||||
|
||||
return (
|
||||
<div
|
||||
className="ppcp-r-navigation--progress-bar"
|
||||
style={ { width: `${ percent * 0.9 }%` } }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navigation;
|
||||
|
|
|
@ -1,40 +1,37 @@
|
|||
import Container from '../../ReusableComponents/Container';
|
||||
import { OnboardingHooks } from '../../../data';
|
||||
import { getSteps } from './availableSteps';
|
||||
|
||||
import { getSteps, getCurrentStep } from './availableSteps';
|
||||
import Navigation from './Components/Navigation';
|
||||
|
||||
const getCurrentStep = ( requestedStep, steps ) => {
|
||||
const isValidStep = ( step ) =>
|
||||
typeof step === 'number' &&
|
||||
Number.isInteger( step ) &&
|
||||
step >= 0 &&
|
||||
step < steps.length;
|
||||
|
||||
const safeCurrentStep = isValidStep( requestedStep ) ? requestedStep : 0;
|
||||
return steps[ safeCurrentStep ];
|
||||
};
|
||||
|
||||
const Onboarding = () => {
|
||||
const { step, setStep, setCompleted, flags } = OnboardingHooks.useSteps();
|
||||
const steps = getSteps( flags );
|
||||
|
||||
const CurrentStepComponent = getCurrentStep( step, steps );
|
||||
const Steps = getSteps( flags );
|
||||
const currentStep = getCurrentStep( step, Steps );
|
||||
|
||||
const handleNext = () => setStep( currentStep.nextStep );
|
||||
const handlePrev = () => setStep( currentStep.prevStep );
|
||||
const handleExit = () => {
|
||||
window.location.href = window.ppcpSettings.wcPaymentsTabUrl;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navigation
|
||||
setStep={ setStep }
|
||||
currentStep={ step }
|
||||
setCompleted={ setCompleted }
|
||||
stepperOrder={ steps }
|
||||
stepDetails={ currentStep }
|
||||
onNext={ handleNext }
|
||||
onPrev={ handlePrev }
|
||||
onExit={ handleExit }
|
||||
/>
|
||||
|
||||
<Container page="onboarding">
|
||||
<div className="ppcp-r-card">
|
||||
<CurrentStepComponent
|
||||
<currentStep.StepComponent
|
||||
setStep={ setStep }
|
||||
currentStep={ step }
|
||||
setCompleted={ setCompleted }
|
||||
stepperOrder={ steps }
|
||||
stepperOrder={ Steps }
|
||||
/>
|
||||
</div>
|
||||
</Container>
|
||||
|
|
|
@ -10,9 +10,8 @@ const BUSINESS_RADIO_GROUP_NAME = 'business';
|
|||
const StepBusiness = ( {} ) => {
|
||||
const { isCasualSeller, setIsCasualSeller } = OnboardingHooks.useBusiness();
|
||||
|
||||
const handleSellerTypeChange = ( value ) => {
|
||||
const handleSellerTypeChange = ( value ) =>
|
||||
setIsCasualSeller( BUSINESS_TYPES.CASUAL_SELLER === value );
|
||||
};
|
||||
|
||||
const getCurrentValue = () => {
|
||||
if ( isCasualSeller === null ) {
|
||||
|
|
|
@ -1,21 +1,86 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import StepWelcome from './StepWelcome';
|
||||
import StepBusiness from './StepBusiness';
|
||||
import StepProducts from './StepProducts';
|
||||
import StepPaymentMethods from './StepPaymentMethods';
|
||||
import StepCompleteSetup from './StepCompleteSetup';
|
||||
|
||||
/**
|
||||
* List of all onboarding screens that are available.
|
||||
*
|
||||
* The screens are displayed in the order in which they appear in this array
|
||||
*
|
||||
* @type {[{id, StepComponent, title}]}
|
||||
*/
|
||||
const ALL_STEPS = [
|
||||
{
|
||||
id: 'welcome',
|
||||
title: __( 'PayPal Payments', 'woocommerce-paypal-payments' ),
|
||||
StepComponent: StepWelcome,
|
||||
canProceed: () => true,
|
||||
},
|
||||
{
|
||||
id: 'business',
|
||||
title: __( 'Set up store type', 'woocommerce-paypal-payments' ),
|
||||
StepComponent: StepBusiness,
|
||||
canProceed: ( { business } ) => business.isCasualSeller !== null,
|
||||
},
|
||||
{
|
||||
id: 'products',
|
||||
title: __( 'Select product types', 'woocommerce-paypal-payments' ),
|
||||
StepComponent: StepProducts,
|
||||
canProceed: ( { products } ) => products.products.length > 0,
|
||||
},
|
||||
{
|
||||
id: 'methods',
|
||||
title: __( 'Choose checkout options', 'woocommerce-paypal-payments' ),
|
||||
StepComponent: StepPaymentMethods,
|
||||
canProceed: () => true,
|
||||
},
|
||||
{
|
||||
id: 'complete',
|
||||
title: __(
|
||||
'Connect your PayPal account',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
StepComponent: StepCompleteSetup,
|
||||
canProceed: () => true,
|
||||
},
|
||||
];
|
||||
|
||||
export const getSteps = ( flags ) => {
|
||||
const allSteps = [
|
||||
StepWelcome,
|
||||
StepBusiness,
|
||||
StepProducts,
|
||||
StepPaymentMethods,
|
||||
StepCompleteSetup,
|
||||
];
|
||||
const steps = flags.canUseCasualSelling
|
||||
? ALL_STEPS
|
||||
: ALL_STEPS.filter( ( step ) => step.id !== 'business' );
|
||||
|
||||
if ( ! flags.canUseCasualSelling ) {
|
||||
return allSteps.filter( ( step ) => step !== StepBusiness );
|
||||
}
|
||||
const totalStepsCount = steps.length;
|
||||
|
||||
return allSteps;
|
||||
return steps.map( ( step, index ) => ( {
|
||||
...step,
|
||||
isFirst: index === 0,
|
||||
isLast: index === totalStepsCount - 1,
|
||||
showNext: index < totalStepsCount - 1,
|
||||
percentage: 100 * ( index / ( totalStepsCount - 1 ) ),
|
||||
nextStep: index < totalStepsCount - 1 ? index + 1 : index,
|
||||
prevStep: index > 0 ? index - 1 : 0,
|
||||
} ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the screen-details of the current step, based on the numeric step-index.
|
||||
*
|
||||
* @param {number} requestedStep Index of the screen to display.
|
||||
* @param {[]} steps List of all available steps (see `getSteps()`)
|
||||
* @return {{id, StepComponent, title}} The requested screen details, or the first welcome screen.
|
||||
*/
|
||||
export const getCurrentStep = ( requestedStep, steps ) => {
|
||||
const isValidStep = ( step ) =>
|
||||
typeof step === 'number' &&
|
||||
Number.isInteger( step ) &&
|
||||
step >= 0 &&
|
||||
step < steps.length;
|
||||
|
||||
const safeCurrentStep = isValidStep( requestedStep ) ? requestedStep : 0;
|
||||
return steps[ safeCurrentStep ];
|
||||
};
|
||||
|
|
|
@ -113,3 +113,13 @@ export const useSteps = () => {
|
|||
|
||||
return { flags, isReady, step, setStep, completed, setCompleted };
|
||||
};
|
||||
|
||||
export const useNavigationState = () => {
|
||||
const products = useProducts();
|
||||
const business = useBusiness();
|
||||
|
||||
return {
|
||||
products,
|
||||
business,
|
||||
};
|
||||
};
|
||||
|
|
44
modules/ppcp-settings/resources/js/hooks/useIsScrolled.js
Normal file
44
modules/ppcp-settings/resources/js/hooks/useIsScrolled.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Taken from WooCommerce core:
|
||||
* https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/client/admin/client/hooks/useIsScrolled.js
|
||||
*/
|
||||
|
||||
import { useEffect, useRef, useState } from '@wordpress/element';
|
||||
|
||||
const isAtBottom = () =>
|
||||
window.innerHeight + window.scrollY >= document.body.scrollHeight;
|
||||
|
||||
const useIsScrolled = () => {
|
||||
const [ isScrolled, setIsScrolled ] = useState( false );
|
||||
const [ atBottom, setAtBottom ] = useState( isAtBottom() );
|
||||
const rafHandle = useRef( null );
|
||||
useEffect( () => {
|
||||
const updateIsScrolled = () => {
|
||||
setIsScrolled( window.pageYOffset > 20 );
|
||||
setAtBottom( isAtBottom() );
|
||||
};
|
||||
|
||||
const scrollListener = () => {
|
||||
rafHandle.current =
|
||||
window.requestAnimationFrame( updateIsScrolled );
|
||||
};
|
||||
|
||||
window.addEventListener( 'scroll', scrollListener );
|
||||
|
||||
window.addEventListener( 'resize', scrollListener );
|
||||
|
||||
return () => {
|
||||
window.removeEventListener( 'scroll', scrollListener );
|
||||
window.removeEventListener( 'resize', scrollListener );
|
||||
window.cancelAnimationFrame( rafHandle.current );
|
||||
};
|
||||
}, [] );
|
||||
|
||||
return {
|
||||
isScrolled,
|
||||
atBottom,
|
||||
atTop: ! isScrolled,
|
||||
};
|
||||
};
|
||||
|
||||
export default useIsScrolled;
|
Loading…
Add table
Add a link
Reference in a new issue