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-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);
|
||||
color: $color-gray-900;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin: 24px auto;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&--content {
|
||||
&__content {
|
||||
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 { 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 = ( {
|
||||
title,
|
||||
initiallyOpen = null,
|
||||
className = '',
|
||||
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 = () => {
|
||||
if ( id && initiallyOpen === null ) {
|
||||
return window.location.hash === `#${ id }`;
|
||||
}
|
||||
return !! initiallyOpen;
|
||||
};
|
||||
const { isOpen, toggleOpen } = useAccordionState( { id, initiallyOpen } );
|
||||
const wrapperClasses = classNames( 'ppcp-r-accordion', className, {
|
||||
'ppcp--is-open': isOpen,
|
||||
} );
|
||||
|
||||
const [ isOpen, setIsOpen ] = useState( determineInitialState );
|
||||
|
||||
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' );
|
||||
}
|
||||
const icon = isOpen ? chevronUp : chevronDown;
|
||||
|
||||
return (
|
||||
<div className={ wrapperClasses.join( ' ' ) } id={ id }>
|
||||
<div className={ wrapperClasses } { ...( id && { id } ) }>
|
||||
<button
|
||||
onClick={ toggleOpen }
|
||||
className="ppcp-r-accordion--title"
|
||||
type="button"
|
||||
className="ppcp-r-accordion__toggler"
|
||||
onClick={ toggleOpen }
|
||||
>
|
||||
<span>{ title }</span>
|
||||
<Icon icon={ isOpen ? chevronUp : chevronDown } />
|
||||
<Header>
|
||||
<TitleWrapper>
|
||||
<Title>{ title }</Title>
|
||||
<Action>
|
||||
<Icon icon={ icon } />
|
||||
</Action>
|
||||
</TitleWrapper>
|
||||
{ description && (
|
||||
<Description>{ description }</Description>
|
||||
) }
|
||||
</Header>
|
||||
</button>
|
||||
{ isOpen && (
|
||||
<div className="ppcp-r-accordion--content">{ children }</div>
|
||||
{ isOpen && children && (
|
||||
<div className="ppcp-r-accordion__content">{ children }</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