learn-how
3 months ago
BsfLearn.js
3 months ago
BsfLearnChapters.js
3 months ago
BsfLearnSkeleton.js
3 months ago
BsfLearnStep.js
3 months ago
LearnHowDialog.js
3 months ago
BsfLearnChapters.js
150 lines
| 1 | import { __, sprintf } from '@wordpress/i18n'; |
| 2 | import { Accordion, Badge, Text } from '@bsf/force-ui'; |
| 3 | import { Check, ChevronRight, ExternalLink } from 'lucide-react'; |
| 4 | import BsfLearnStep from './BsfLearnStep'; |
| 5 | |
| 6 | const BsfLearnChapters = ( { chapters, defaultValue, onStepCompletionChange, onLearnHowClick } ) => { |
| 7 | |
| 8 | const handleLearnHowClick = ( event, url ) => { |
| 9 | event.stopPropagation(); |
| 10 | if ( url ) { |
| 11 | window.open( url, '_blank', 'noopener,noreferrer' ); |
| 12 | return; |
| 13 | } |
| 14 | }; |
| 15 | |
| 16 | return ( |
| 17 | <Accordion |
| 18 | type="boxed" |
| 19 | autoClose={ true } |
| 20 | defaultValue={ defaultValue } |
| 21 | > |
| 22 | { chapters.map( ( chapter ) => { |
| 23 | const { |
| 24 | id, |
| 25 | title, |
| 26 | description, |
| 27 | url, |
| 28 | steps, |
| 29 | } = chapter; |
| 30 | |
| 31 | const totalStepsCount = steps.length; |
| 32 | const completedStepsCount = steps.filter( ( step ) => step.completed ).length; |
| 33 | const isCompleted = totalStepsCount === completedStepsCount; |
| 34 | |
| 35 | const getBadgeColor = () => { |
| 36 | if ( isCompleted ) { |
| 37 | return 'green'; |
| 38 | } |
| 39 | if ( completedStepsCount > 0 ) { |
| 40 | return 'blue'; |
| 41 | } |
| 42 | return 'gray'; |
| 43 | }; |
| 44 | |
| 45 | return ( |
| 46 | <Accordion.Item |
| 47 | key={ id } |
| 48 | className="bg-background-primary border-0.5 [&:hover>h3]:bg-transparent rounded-lg overflow-hidden [&:has([aria-expanded='true'])]:shadow-xs transition-all duration-200 ease-in-out" |
| 49 | value={ id } |
| 50 | > |
| 51 | <Accordion.Trigger className="group p-3 sm:p-4 hover:bg-transparent [&>svg]:hidden [&>div]:flex-grow [&[aria-expanded='true']_.learn-chevron-right]:rotate-90 [&[aria-expanded='true']_.learn-more-btn]:flex"> |
| 52 | <div className="flex items-center gap-2 sm:gap-3 flex-1"> |
| 53 | <ChevronRight |
| 54 | className="learn-chevron-right transition-transform duration-200 ease-in-out" |
| 55 | size={ 20 } |
| 56 | /> |
| 57 | |
| 58 | <div className="flex-1 text-left"> |
| 59 | <Text size={ 14 } className="sm:text-base" weight={ 600 }> |
| 60 | { title } |
| 61 | </Text> |
| 62 | </div> |
| 63 | |
| 64 | { url && ( |
| 65 | <span |
| 66 | className="hover:underline underline-offset-2 learn-more-btn hidden transition-all cursor-pointer text-link-primary outline-link-primary items-center gap-1 text-xs px-2 py-1" |
| 67 | onClick={ ( event ) => handleLearnHowClick( event, url ) } |
| 68 | role="link" |
| 69 | tabIndex={ 0 } |
| 70 | onKeyDown={ ( e ) => { |
| 71 | if ( e.key === 'Enter' || e.key === ' ' ) { |
| 72 | handleLearnHowClick( e, url ); |
| 73 | } |
| 74 | } } |
| 75 | > |
| 76 | <span className="hidden sm:inline"> |
| 77 | { __( 'Learn how', 'presto-player' ) } |
| 78 | </span> |
| 79 | <ExternalLink size={ 16 } strokeWidth={ 1.25 } /> |
| 80 | </span> |
| 81 | ) } |
| 82 | |
| 83 | <Badge |
| 84 | className="relative overflow-hidden w-14 sm:w-[62px] text-xs" |
| 85 | label={ |
| 86 | <> |
| 87 | <span className="sr-only"> |
| 88 | { sprintf( |
| 89 | __( '%1$d of %2$d steps completed', 'presto-player' ), |
| 90 | completedStepsCount, |
| 91 | totalStepsCount |
| 92 | ) } |
| 93 | </span> |
| 94 | |
| 95 | <span className="flex items-center"> |
| 96 | { isCompleted && <Check size={ 12 } /> } |
| 97 | <span className="px-1 relative z-10"> |
| 98 | { completedStepsCount }/{ totalStepsCount } |
| 99 | </span> |
| 100 | </span> |
| 101 | |
| 102 | <span |
| 103 | className="absolute h-full top-0 left-0 bg-[#BAE6FD]/40 transition-[width] duration-300 ease-in-out" |
| 104 | style={ { |
| 105 | width: `${ ( completedStepsCount / totalStepsCount ) * 100 }%`, |
| 106 | } } |
| 107 | /> |
| 108 | </> |
| 109 | } |
| 110 | variant={ getBadgeColor() } |
| 111 | /> |
| 112 | </div> |
| 113 | </Accordion.Trigger> |
| 114 | |
| 115 | <Accordion.Content className="overflow-visible [&>div]:p-0"> |
| 116 | { description && ( |
| 117 | <Text |
| 118 | className="px-3 sm:px-4 ml-7 mr-0 sm:mx-8 -mt-2 pb-4 max-w-full sm:max-w-[72%]" |
| 119 | size={ 14 } |
| 120 | color="secondary" |
| 121 | weight={ 400 } |
| 122 | > |
| 123 | { description } |
| 124 | </Text> |
| 125 | ) } |
| 126 | |
| 127 | <span className="block w-full h-[0.5px] bg-border-subtle" /> |
| 128 | |
| 129 | <div className="px-3 sm:px-4 flex flex-col bg-[#FCFCFD]"> |
| 130 | { steps.map( ( step, index ) => ( |
| 131 | <BsfLearnStep |
| 132 | key={ step.id } |
| 133 | step={ step } |
| 134 | chapterId={ id } |
| 135 | isLast={ index === steps.length - 1 } |
| 136 | onCompletionChange={ onStepCompletionChange } |
| 137 | onLearnHowClick={ onLearnHowClick } |
| 138 | /> |
| 139 | ) ) } |
| 140 | </div> |
| 141 | </Accordion.Content> |
| 142 | </Accordion.Item> |
| 143 | ); |
| 144 | } ) } |
| 145 | </Accordion> |
| 146 | ); |
| 147 | }; |
| 148 | |
| 149 | export default BsfLearnChapters; |
| 150 |