mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
♻️ Make generic Accordion more generic
This commit is contained in:
parent
7146163301
commit
1b557f1619
3 changed files with 106 additions and 59 deletions
|
@ -2,26 +2,27 @@
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
|
||||||
&--title {
|
&__toggler {
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title-wrapper {
|
||||||
@include font(14, 32, 450);
|
@include font(14, 32, 450);
|
||||||
color: $color-gray-900;
|
color: $color-gray-900;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
margin: 24px auto;
|
|
||||||
border: 0;
|
|
||||||
background: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--content {
|
&__content {
|
||||||
margin: 24px 0 0;
|
margin: 24px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ppcp--is-open {
|
|
||||||
.ppcp-r-accordion--icon {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +1,72 @@
|
||||||
import { useEffect } from '@wordpress/element';
|
|
||||||
import { Icon } from '@wordpress/components';
|
import { Icon } from '@wordpress/components';
|
||||||
import { chevronDown, chevronUp } from '@wordpress/icons';
|
import { chevronDown, chevronUp } from '@wordpress/icons';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { useAccordionState } from '../../hooks/useAccordionState';
|
||||||
|
|
||||||
|
// Provide defaults for all layout components so the generic version just works.
|
||||||
|
const DefaultHeader = ( { children, className = '' } ) => (
|
||||||
|
<div className={ `ppcp-r-accordion__header ${ className }`.trim() }>
|
||||||
|
{ children }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const DefaultTitleWrapper = ( { children } ) => (
|
||||||
|
<div className="ppcp-r-accordion__title-wrapper">{ children }</div>
|
||||||
|
);
|
||||||
|
const DefaultTitle = ( { children } ) => (
|
||||||
|
<span className="ppcp-r-accordion__title">{ children }</span>
|
||||||
|
);
|
||||||
|
const DefaultAction = ( { children } ) => (
|
||||||
|
<span className="ppcp-r-accordion__action">{ children }</span>
|
||||||
|
);
|
||||||
|
const DefaultDescription = ( { children } ) => (
|
||||||
|
<div className="ppcp-r-accordion__description">{ children }</div>
|
||||||
|
);
|
||||||
|
|
||||||
const Accordion = ( {
|
const Accordion = ( {
|
||||||
title,
|
title,
|
||||||
initiallyOpen = null,
|
|
||||||
className = '',
|
|
||||||
id = '',
|
id = '',
|
||||||
children,
|
initiallyOpen = null,
|
||||||
|
description = '',
|
||||||
|
children = null,
|
||||||
|
className = '',
|
||||||
|
|
||||||
|
// Layout components can be overridden by the caller
|
||||||
|
Header = DefaultHeader,
|
||||||
|
TitleWrapper = DefaultTitleWrapper,
|
||||||
|
Title = DefaultTitle,
|
||||||
|
Action = DefaultAction,
|
||||||
|
Description = DefaultDescription,
|
||||||
} ) => {
|
} ) => {
|
||||||
const determineInitialState = () => {
|
const { isOpen, toggleOpen } = useAccordionState( { id, initiallyOpen } );
|
||||||
if ( id && initiallyOpen === null ) {
|
const wrapperClasses = classNames( 'ppcp-r-accordion', className, {
|
||||||
return window.location.hash === `#${ id }`;
|
'ppcp--is-open': isOpen,
|
||||||
}
|
} );
|
||||||
return !! initiallyOpen;
|
|
||||||
};
|
|
||||||
|
|
||||||
const [ isOpen, setIsOpen ] = useState( determineInitialState );
|
const icon = isOpen ? chevronUp : chevronDown;
|
||||||
|
|
||||||
useEffect( () => {
|
|
||||||
const handleHashChange = () => {
|
|
||||||
if ( id && window.location.hash === `#${ id }` ) {
|
|
||||||
setIsOpen( true );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener( 'hashchange', handleHashChange );
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener( 'hashchange', handleHashChange );
|
|
||||||
};
|
|
||||||
}, [ id ] );
|
|
||||||
|
|
||||||
const toggleOpen = ( ev ) => {
|
|
||||||
setIsOpen( ! isOpen );
|
|
||||||
ev?.preventDefault();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const wrapperClasses = [ 'ppcp-r-accordion' ];
|
|
||||||
if ( className ) {
|
|
||||||
wrapperClasses.push( className );
|
|
||||||
}
|
|
||||||
if ( isOpen ) {
|
|
||||||
wrapperClasses.push( 'ppcp--is-open' );
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ wrapperClasses.join( ' ' ) } id={ id }>
|
<div className={ wrapperClasses } { ...( id && { id } ) }>
|
||||||
<button
|
<button
|
||||||
onClick={ toggleOpen }
|
|
||||||
className="ppcp-r-accordion--title"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
className="ppcp-r-accordion__toggler"
|
||||||
|
onClick={ toggleOpen }
|
||||||
>
|
>
|
||||||
<span>{ title }</span>
|
<Header>
|
||||||
<Icon icon={ isOpen ? chevronUp : chevronDown } />
|
<TitleWrapper>
|
||||||
|
<Title>{ title }</Title>
|
||||||
|
<Action>
|
||||||
|
<Icon icon={ icon } />
|
||||||
|
</Action>
|
||||||
|
</TitleWrapper>
|
||||||
|
{ description && (
|
||||||
|
<Description>{ description }</Description>
|
||||||
|
) }
|
||||||
|
</Header>
|
||||||
</button>
|
</button>
|
||||||
{ isOpen && (
|
{ isOpen && children && (
|
||||||
<div className="ppcp-r-accordion--content">{ children }</div>
|
<div className="ppcp-r-accordion__content">{ children }</div>
|
||||||
) }
|
) }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { useEffect, useState } from '@wordpress/element';
|
||||||
|
|
||||||
|
const checkIfCurrentTab = ( id ) => {
|
||||||
|
return id && window.location.hash === `#${ id }`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const determineInitialState = ( id, initiallyOpen ) => {
|
||||||
|
if ( initiallyOpen !== null ) {
|
||||||
|
return initiallyOpen;
|
||||||
|
}
|
||||||
|
return checkIfCurrentTab( id );
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useAccordionState( { id = '', initiallyOpen = null } ) {
|
||||||
|
const [ isOpen, setIsOpen ] = useState(
|
||||||
|
determineInitialState( id, initiallyOpen )
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
const handleHashChange = () => {
|
||||||
|
if ( checkIfCurrentTab( id ) ) {
|
||||||
|
setIsOpen( true );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener( 'hashchange', handleHashChange );
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener( 'hashchange', handleHashChange );
|
||||||
|
};
|
||||||
|
}, [ id ] );
|
||||||
|
|
||||||
|
const toggleOpen = ( ev ) => {
|
||||||
|
setIsOpen( ! isOpen );
|
||||||
|
ev?.preventDefault();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { isOpen, toggleOpen };
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue