give
/
src
/
DonationForms
/
resources
/
registrars
/
templates
/
fields
/
Amount
/
DonationAmountLevels.tsx
CurrencySwitcher.tsx
2 years ago
CustomAmount.tsx
1 year ago
DonationAmountCurrency.tsx
2 years ago
DonationAmountLevels.tsx
1 month ago
index.tsx
1 month ago
withCurrencySettings.tsx
1 month ago
DonationAmountLevels.tsx
120 lines
| 1 | import classNames from 'classnames'; |
| 2 | import {__} from '@wordpress/i18n'; |
| 3 | import {useEffect, useState} from '@wordpress/element'; |
| 4 | |
| 5 | /** |
| 6 | * @since 3.0.0 |
| 7 | */ |
| 8 | type DonationAmountLevelsProps = { |
| 9 | name: string; |
| 10 | currency: string; |
| 11 | levels: Level[]; |
| 12 | onLevelClick?: (amount: number, index: number) => void; |
| 13 | }; |
| 14 | |
| 15 | type GroupedLevels = { |
| 16 | labeled: Level[]; |
| 17 | unlabeled: Level[]; |
| 18 | }; |
| 19 | |
| 20 | type Level = {label: string | null; value: number; checked?: boolean}; |
| 21 | |
| 22 | /** |
| 23 | * Prefer the level marked as checked in the form builder when amounts are duplicated. |
| 24 | * |
| 25 | * @since 4.16.5 |
| 26 | */ |
| 27 | function getSelectedLevelIndex(levels: Level[], amount: unknown): number { |
| 28 | const checkedIndex = levels.findIndex((level) => level.checked); |
| 29 | |
| 30 | if (checkedIndex >= 0 && levels[checkedIndex].value === amount) { |
| 31 | return checkedIndex; |
| 32 | } |
| 33 | |
| 34 | const matchingIndices = levels.reduce<number[]>((indices, level, index) => { |
| 35 | if (level.value === amount) { |
| 36 | indices.push(index); |
| 37 | } |
| 38 | |
| 39 | return indices; |
| 40 | }, []); |
| 41 | |
| 42 | return matchingIndices.length === 1 ? matchingIndices[0] : -1; |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * @since 4.16.5 Track the selected level by index to support duplicate amounts. |
| 47 | * @since 4.3.0 Add proper roles and ARIA attributes |
| 48 | * @since 3.12.0 add level descriptions. |
| 49 | * @since 3.0.0 |
| 50 | */ |
| 51 | export default function DonationAmountLevels({name, currency, levels, onLevelClick}: DonationAmountLevelsProps) { |
| 52 | const {useWatch, useCurrencyFormatter} = window.givewp.form.hooks; |
| 53 | const amount = useWatch({name}); |
| 54 | const formatter = useCurrencyFormatter(currency); |
| 55 | |
| 56 | const groupedLevels: GroupedLevels = levels.reduce( |
| 57 | (acc: GroupedLevels, level) => { |
| 58 | const key = level.label ? 'labeled' : 'unlabeled'; |
| 59 | acc[key].push(level); |
| 60 | return acc; |
| 61 | }, |
| 62 | {labeled: [], unlabeled: []} |
| 63 | ); |
| 64 | |
| 65 | const allLevels = [...groupedLevels.labeled, ...groupedLevels.unlabeled]; |
| 66 | |
| 67 | const [selectedIndex, setSelectedIndex] = useState<number>(() => getSelectedLevelIndex(allLevels, amount)); |
| 68 | |
| 69 | useEffect(() => { |
| 70 | if (allLevels[selectedIndex]?.value !== amount) { |
| 71 | setSelectedIndex(getSelectedLevelIndex(allLevels, amount)); |
| 72 | } |
| 73 | }, [levels, amount]); |
| 74 | |
| 75 | return ( |
| 76 | <div |
| 77 | className={classNames('givewp-fields-amount__levels-container', { |
| 78 | 'givewp-fields-amount__levels-container--has-descriptions': groupedLevels.labeled.length > 0, |
| 79 | })} |
| 80 | role="radiogroup" |
| 81 | aria-label={__('Donation Amount', 'give')} |
| 82 | > |
| 83 | {allLevels.map((level, index) => { |
| 84 | const label = formatter.format(level.value); |
| 85 | const selected = index === selectedIndex; |
| 86 | const hasDescription = level.label; |
| 87 | |
| 88 | return ( |
| 89 | <div |
| 90 | className={classNames('givewp-fields-amount__level-container', { |
| 91 | 'givewp-fields-amount__level-container--col': hasDescription, |
| 92 | })} |
| 93 | key={index} |
| 94 | > |
| 95 | <button |
| 96 | className={classNames('givewp-fields-amount__level', { |
| 97 | 'givewp-fields-amount__level--selected': selected, |
| 98 | 'givewp-fields-amount__level--description': hasDescription, |
| 99 | })} |
| 100 | type="button" |
| 101 | role="radio" |
| 102 | aria-checked={selected} |
| 103 | onClick={() => { |
| 104 | setSelectedIndex(index); |
| 105 | onLevelClick(level.value, index); |
| 106 | }} |
| 107 | key={index} |
| 108 | > |
| 109 | {label} |
| 110 | </button> |
| 111 | {hasDescription && ( |
| 112 | <span className={'givewp-fields-amount__level__description'}>{level.label}</span> |
| 113 | )} |
| 114 | </div> |
| 115 | ); |
| 116 | })} |
| 117 | </div> |
| 118 | ); |
| 119 | } |
| 120 |