@axos-web-dev/shared-components 2.0.0-dev.24 → 2.0.0-dev.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/ATMLocator/ATMLocator.js +1 -1
  2. package/dist/Auth/ErrorAlert.js +1 -1
  3. package/dist/Button/Button.js +1 -1
  4. package/dist/Calculators/AnnualFeeCalculator/index.js +1 -1
  5. package/dist/Calculators/ApyCalculator/ApyCalculator.css.d.ts +45 -5
  6. package/dist/Calculators/ApyCalculator/ApyCalculator.css.js +70 -19
  7. package/dist/Calculators/ApyCalculator/index.js +562 -293
  8. package/dist/Calculators/Calculator.js +2 -2
  9. package/dist/Calculators/MarginTradingCalculator/index.js +1 -1
  10. package/dist/Calculators/MarineLoanMonthlyPaymentCalculator/index.js +1 -1
  11. package/dist/Calculators/MaxLoanCalculator/index.js +1 -1
  12. package/dist/Calculators/MonthlyPaymentCalculator/index.js +1 -1
  13. package/dist/Calculators/calculator.css.d.ts +21 -1
  14. package/dist/Calculators/calculator.css.js +3 -3
  15. package/dist/Carousel/index.js +2 -2
  16. package/dist/Chevron/index.js +1 -1
  17. package/dist/Comparison/Comparison.js +1 -1
  18. package/dist/ExecutiveBio/ExecutiveBio.js +1 -1
  19. package/dist/FaqAccordion/index.js +1 -1
  20. package/dist/FooterSiteMap/AxosBank/FooterSiteMap.js +1 -1
  21. package/dist/Forms/ApplyNow.js +2 -2
  22. package/dist/Forms/ContactUsBusiness.js +1 -1
  23. package/dist/Forms/ContactUsBusinessNameEmail.js +1 -1
  24. package/dist/Forms/ContactUsNMLSId.js +1 -1
  25. package/dist/Forms/CpraRequest.js +1 -1
  26. package/dist/Forms/CraPublicFile.js +1 -1
  27. package/dist/Forms/EmailOnly.js +1 -1
  28. package/dist/Forms/MortgageRate/MortgageRateForm.js +2 -2
  29. package/dist/Forms/MortgageRate/MortgageRateWatch.js +2 -2
  30. package/dist/Forms/MortgageWarehouseLending.js +1 -1
  31. package/dist/Forms/SuccesForm.js +2 -2
  32. package/dist/Hyperlink/index.js +1 -1
  33. package/dist/ImageLink/ImageLink.js +2 -2
  34. package/dist/ImageLink/ImageLinkSet.js +1 -1
  35. package/dist/ImageLink/index.js +1 -1
  36. package/dist/Insight/Featured/CategorySelector.js +2 -2
  37. package/dist/Insight/Featured/Featured.js +2 -2
  38. package/dist/Insight/Featured/Header.js +2 -2
  39. package/dist/Interstitial/Interstitial.module.js +10 -10
  40. package/dist/Modal/Modal.js +1 -1
  41. package/dist/NavigationMenu/AxosALTS/index.js +1 -1
  42. package/dist/NavigationMenu/AxosBank/MobileMenu/MobileMenu.js +1 -1
  43. package/dist/NavigationMenu/AxosBank/MobileMenu/MobileMenu.module.js +27 -27
  44. package/dist/NavigationMenu/AxosBank/MobileMenu/MobileNavData.d.ts +1 -0
  45. package/dist/NavigationMenu/AxosBank/MobileMenu/MobileNavData.js +4 -3
  46. package/dist/NavigationMenu/AxosBank/SubNavBar.js +24 -3
  47. package/dist/NavigationMenu/AxosBank/index.js +1 -1
  48. package/dist/NavigationMenu/LaVictoire/index.js +1 -1
  49. package/dist/NavigationMenu/Navbar.js +1 -1
  50. package/dist/NavigationMenu/SignInNavButton.js +1 -1
  51. package/dist/SetContainer/SetContainer.css.d.ts +1 -0
  52. package/dist/SetContainer/SetContainer.css.js +5 -3
  53. package/dist/SetContainer/SetContainer.js +4 -4
  54. package/dist/SetContainer/index.js +2 -1
  55. package/dist/SocialMediaBar/iconsRepository.js +1 -1
  56. package/dist/VideoTile/VideoTile.js +1 -1
  57. package/dist/VideoWrapper/index.js +1 -1
  58. package/dist/assets/Calculators/ApyCalculator/ApyCalculator.css +347 -64
  59. package/dist/assets/Calculators/calculator.css +25 -15
  60. package/dist/assets/ImageBillboard/ImageBillboard.css +1 -1
  61. package/dist/assets/Interstitial/Interstitial.css +142 -142
  62. package/dist/assets/NavigationMenu/AxosBank/MobileMenu/MobileMenu.css +353 -353
  63. package/dist/assets/SetContainer/SetContainer.css +22 -15
  64. package/dist/main.js +2 -1
  65. package/package.json +1 -1
@@ -1,327 +1,596 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { Button } from "../../Button/Button.js";
4
- import { button } from "../../Button/Button.css.js";
5
- import { useState, useCallback, useEffect } from "react";
6
- import "react-use";
7
- import { Chevron } from "../../Chevron/index.js";
8
- import { section_container, content, headerIconBillboard, buttons } from "../../IconBillboard/IconBillboard.css.js";
2
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
9
3
  import { useGlobalContext } from "../../Modal/contextApi/store.js";
10
4
  import { getVariant } from "../../utils/getVariant.js";
11
- import { apy_calculator, calculator_section, section_header, header_theme, mt_8, apy_calculator_form, pis_0, errorTag, fieldset, field_row, relative, label_symbol, cash, prefix_pad, percent, submit_section, span_12, form_disclosure, marketing, marketingTile, bodyContent, container } from "./ApyCalculator.css.js";
5
+ import Image from "next/image.js";
6
+ import { Field, Label, Input, Description, Listbox, ListboxButton, ListboxOptions, ListboxOption, Disclosure, DisclosureButton, DisclosurePanel } from "@headlessui/react";
7
+ import { AnimatePresence, motion } from "framer-motion";
8
+ import { useState, useCallback, useEffect, useRef } from "react";
9
+ import { apy_calculator, section_header, header_theme, mt_8, calculator_section, apy_calculator_form, fieldset, field_row, relative, field_label, field_row_input, field_row_input_error, field_error, label_symbol, percent, listbox_button, chevron_icon, listbox_options, listbox_option, optional_badge, form_disclosure, results_wrapper, result_card, result_section, result_image, result_value, scrambling, result_value_label, result_item, result_item_label, result_item_value, definitions_accordion, definitions_button, definitions_h2, definitions_columns, definitions_panel, definitions_column, definitions_item, definitions_term, definitions_text, container } from "./ApyCalculator.css.js";
10
+ const COMPOUNDING_OPTIONS = [
11
+ { value: 360, label: "Daily" },
12
+ { value: 12, label: "Monthly" },
13
+ { value: 4, label: "Quarterly" },
14
+ { value: 2, label: "Semi-Annual" },
15
+ { value: 1, label: "Yearly" }
16
+ ];
17
+ const CALCULATOR_DEFINITIONS = [
18
+ {
19
+ term: "Initial deposit",
20
+ description: "The amount of money you're starting with."
21
+ },
22
+ {
23
+ term: "APY (Annual Percentage Yield)",
24
+ description: "The rate your money earns in a year, with compounding included."
25
+ },
26
+ {
27
+ term: "Months",
28
+ description: "How long you plan to keep your money in the account."
29
+ },
30
+ {
31
+ term: "Compounding",
32
+ description: "How often interest is calculated and added to your balance. APY on bank accounts usually compounds monthly."
33
+ },
34
+ {
35
+ term: "Monthly deposits",
36
+ description: "Any additional amount you plan to contribute each month."
37
+ },
38
+ {
39
+ term: "Interest earned",
40
+ description: "The amount gained from interest over the selected time period."
41
+ },
42
+ {
43
+ term: "Ending balance",
44
+ description: "Your projected balance at the end of the selected time period, based on the inputs above."
45
+ },
46
+ {
47
+ term: "Total deposits",
48
+ description: "The total money you contribute, including your initial and monthly deposits."
49
+ }
50
+ ];
51
+ const SCRAMBLE_CHARS = "0123456789";
52
+ const DEFINITIONS_ITEM_TRANSITION = {
53
+ duration: 0.35,
54
+ ease: [0.22, 1, 0.36, 1]
55
+ };
56
+ function scrambleCurrency(target, progress) {
57
+ const formatted = target.toLocaleString("en-US", {
58
+ style: "currency",
59
+ currency: "USD"
60
+ });
61
+ return formatted.split("").map((char) => {
62
+ if (char === "$" || char === "," || char === "." || char === "-") {
63
+ return char;
64
+ }
65
+ if (/\d/.test(char)) {
66
+ return Math.random() > progress ? SCRAMBLE_CHARS[Math.floor(Math.random() * SCRAMBLE_CHARS.length)] : char;
67
+ }
68
+ return char;
69
+ }).join("");
70
+ }
71
+ function useScramble(targetValue, duration = 700) {
72
+ const [display, setDisplay] = useState(
73
+ () => targetValue.toLocaleString("en-US", { style: "currency", currency: "USD" })
74
+ );
75
+ const [isScrambling, setIsScrambling] = useState(false);
76
+ const rafRef = useRef(null);
77
+ const startRef = useRef(0);
78
+ const prevTarget = useRef(targetValue);
79
+ useEffect(() => {
80
+ if (prevTarget.current === targetValue) return;
81
+ prevTarget.current = targetValue;
82
+ setIsScrambling(true);
83
+ startRef.current = Date.now();
84
+ const tick = () => {
85
+ const elapsed = Date.now() - startRef.current;
86
+ const progress = Math.min(elapsed / duration, 1);
87
+ if (progress < 1) {
88
+ setDisplay(scrambleCurrency(targetValue, progress));
89
+ rafRef.current = setTimeout(tick, 40);
90
+ } else {
91
+ setDisplay(
92
+ targetValue.toLocaleString("en-US", {
93
+ style: "currency",
94
+ currency: "USD"
95
+ })
96
+ );
97
+ setIsScrambling(false);
98
+ }
99
+ };
100
+ if (rafRef.current) clearTimeout(rafRef.current);
101
+ rafRef.current = setTimeout(tick, 40);
102
+ return () => {
103
+ if (rafRef.current) clearTimeout(rafRef.current);
104
+ };
105
+ }, [targetValue, duration]);
106
+ return { display, isScrambling };
107
+ }
108
+ function formatWithCommas(value) {
109
+ return value.toLocaleString("en-US");
110
+ }
111
+ function parseCurrencyInput(raw) {
112
+ const digits = raw.replace(/[^0-9]/g, "");
113
+ return digits === "" ? 0 : parseInt(digits, 10);
114
+ }
12
115
  const ApyCalculator = ({
13
116
  header,
14
117
  body,
15
- marketingTiles,
16
118
  disclosure,
17
119
  variant
18
120
  }) => {
19
121
  const { domains } = useGlobalContext();
20
122
  const calculator_variant = getVariant(variant);
21
- const [compounding, setCompounding] = useState(360);
22
- const getAPR = (apy) => {
23
- return Number.parseFloat(
24
- (((1 + apy / 100) ** (1 / compounding) - 1) * compounding * 100).toFixed(
25
- 2
26
- )
27
- );
28
- };
29
- const AXOS_ONE_APY = Number(domains.AXOS_ONE_APY);
30
- const AXOS_ONE_APR = getAPR(AXOS_ONE_APY);
31
- const [initialDeposit, setInititalDeposit] = useState(1e3);
32
- const [APR, setAPR] = useState(AXOS_ONE_APR);
123
+ const [compoundingOption, setCompoundingOption] = useState(
124
+ COMPOUNDING_OPTIONS[0]
125
+ );
126
+ const compounding = compoundingOption.value;
127
+ const getAPR = useCallback(
128
+ (apy) => {
129
+ return Number.parseFloat(
130
+ (((1 + apy / 100) ** (1 / compounding) - 1) * compounding * 100).toFixed(2)
131
+ );
132
+ },
133
+ [compounding]
134
+ );
135
+ const AXOS_ONE_APY = Number(domains.AXOS_ONE_APY) || 4.21;
136
+ const [initialDeposit, setInitialDeposit] = useState(1e4);
137
+ const [initialDepositDisplay, setInitialDepositDisplay] = useState(
138
+ formatWithCommas(1e4)
139
+ );
33
140
  const [APY, setAPY] = useState(AXOS_ONE_APY);
141
+ const [APYDisplay, setAPYDisplay] = useState(APY.toString());
34
142
  const [months, setMonths] = useState(12);
143
+ const [monthsDisplay, setMonthsDisplay] = useState(months.toString());
35
144
  const [monthlyDeposits, setMonthlyDeposits] = useState(100);
36
- const [endingBalance, setEndingBalance] = useState("");
37
- const [errors, setErrors] = useState([]);
38
- const [interestRate, setInterestRate] = useState(0);
39
- const isValidNumber = (input) => {
40
- if (typeof input !== "number") {
41
- return false;
42
- } else if (input < 0 || Number.isNaN(input)) {
43
- return false;
44
- } else return true;
45
- };
46
- const isValidAPY = (input) => {
47
- if (typeof input !== "number") {
48
- return false;
49
- } else if (input <= 0 || Number.isNaN(input)) {
50
- return false;
51
- } else return true;
145
+ const [monthlyDepositsDisplay, setMonthlyDepositsDisplay] = useState(
146
+ formatWithCommas(100)
147
+ );
148
+ const [fieldErrors, setFieldErrors] = useState({});
149
+ const handleInitialDepositChange = (e) => {
150
+ const raw = e.target.value;
151
+ const num = parseCurrencyInput(raw);
152
+ setInitialDeposit(num);
153
+ setInitialDepositDisplay(num > 0 ? formatWithCommas(num) : "");
52
154
  };
53
- const convertInterest = useCallback(() => {
54
- const newInterestRate = 0.01 * APR;
55
- setInterestRate(newInterestRate);
56
- }, [APR]);
57
- useEffect(() => {
58
- convertInterest();
59
- }, [convertInterest, interestRate, setInterestRate]);
60
- const handleCalculate = () => {
61
- convertInterest();
62
- let newErrors = [];
63
- if (!isValidNumber(initialDeposit)) {
64
- newErrors.push("Initial Deposit must be a positive number.");
155
+ const handleMonthlyDepositsChange = (e) => {
156
+ const raw = e.target.value;
157
+ if (raw === "") {
158
+ setMonthlyDeposits(0);
159
+ setMonthlyDepositsDisplay("");
160
+ return;
65
161
  }
66
- if (!isValidNumber(months)) {
67
- newErrors.push("Months must be a positive number.");
162
+ const num = parseCurrencyInput(raw);
163
+ setMonthlyDeposits(num);
164
+ setMonthlyDepositsDisplay(formatWithCommas(num));
165
+ };
166
+ const handleMonthsChange = (e) => {
167
+ const raw = e.target.value;
168
+ const num = parseInt(raw.replace(/[^0-9]/g, ""), 10);
169
+ setMonths(num);
170
+ setMonthsDisplay(num > 0 ? num.toString() : "");
171
+ };
172
+ const handleAPYChange = (e) => {
173
+ const raw = e.target.value;
174
+ const num = parseFloat(raw.replace(/[^0-9.]/g, ""));
175
+ setAPY(num);
176
+ setAPYDisplay(raw.replace(/[^0-9.]/g, ""));
177
+ };
178
+ const [endingBalanceRaw, setEndingBalanceRaw] = useState(0);
179
+ const [totalDepositsRaw, setTotalDepositsRaw] = useState(0);
180
+ const [interestEarnedRaw, setInterestEarnedRaw] = useState(0);
181
+ const { display: endingBalanceDisplay, isScrambling: balanceScrambling } = useScramble(endingBalanceRaw);
182
+ const { display: totalDepositsDisplay, isScrambling: depositsScrambling } = useScramble(totalDepositsRaw);
183
+ const { display: interestEarnedDisplay, isScrambling: interestScrambling } = useScramble(interestEarnedRaw);
184
+ const midPoint = Math.ceil(CALCULATOR_DEFINITIONS.length / 2);
185
+ const leftDefinitions = CALCULATOR_DEFINITIONS.slice(0, midPoint);
186
+ const rightDefinitions = CALCULATOR_DEFINITIONS.slice(midPoint);
187
+ const calculate = useCallback(() => {
188
+ const newErrors = {};
189
+ if (!initialDeposit || initialDeposit <= 0 || Number.isNaN(initialDeposit)) {
190
+ newErrors.initialDeposit = "Please enter an amount greater than 0.";
68
191
  }
69
- if (!isValidNumber(monthlyDeposits)) {
70
- newErrors.push("Monthly Deposits must be a positive number.");
192
+ if (!months || months < 1 || Number.isNaN(months)) {
193
+ newErrors.months = "Please enter a term of at least 1 month.";
71
194
  }
72
- if (!isValidAPY(APY)) {
73
- newErrors.push("APY must be a positive number.");
195
+ if (monthlyDeposits < 0 || Number.isNaN(monthlyDeposits)) {
196
+ newErrors.monthlyDeposits = "Monthly deposit amount cannot be negative.";
74
197
  }
75
- if (!isValidAPY(APR)) {
76
- newErrors.push("APR must be a positive number.");
198
+ if (!APY || APY <= 0 || Number.isNaN(APY)) {
199
+ newErrors.APY = "Please enter a valid APY greater than 0.";
77
200
  }
78
- if (newErrors.length > 0) {
79
- setErrors(newErrors);
201
+ setFieldErrors(newErrors);
202
+ if (Object.keys(newErrors).length > 0) return;
203
+ const APR = getAPR(APY);
204
+ const interestRate = 0.01 * APR;
205
+ let total;
206
+ if (compounding === 360) {
207
+ total = initialDeposit * Math.pow(1 + interestRate / compounding, 30 * months) + (monthlyDeposits > 0 ? monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, 30 * months) - 1) : 0);
208
+ } else if (compounding === 12) {
209
+ total = initialDeposit * Math.pow(1 + interestRate / compounding, months) + (monthlyDeposits > 0 ? monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months) - 1) : 0);
210
+ } else if (compounding === 4) {
211
+ total = initialDeposit * Math.pow(1 + interestRate / compounding, months / 3) + (monthlyDeposits > 0 ? monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months / 3) - 1) : 0);
212
+ } else if (compounding === 2) {
213
+ total = initialDeposit * Math.pow(1 + interestRate / compounding, months / 6) + (monthlyDeposits > 0 ? monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months / 6) - 1) : 0);
214
+ } else if (compounding === 1) {
215
+ total = initialDeposit * Math.pow(1 + interestRate, months / 12) + (monthlyDeposits > 0 ? monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months / 12) - 1) : 0);
80
216
  } else {
81
- newErrors = [];
82
- setErrors([]);
83
- let total;
84
- if (compounding === 360) {
85
- total = initialDeposit * Math.pow(1 + interestRate / compounding, 30 * months) + monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, 30 * months) - 1);
86
- } else if (compounding === 12) {
87
- total = initialDeposit * Math.pow(1 + interestRate / compounding, months) + monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months) - 1);
88
- } else if (compounding === 4) {
89
- total = initialDeposit * Math.pow(1 + interestRate / compounding, months / 3) + monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months / 3) - 1);
90
- } else if (compounding === 2) {
91
- total = initialDeposit * Math.pow(1 + interestRate / compounding, months / 6) + monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months / 6) - 1);
92
- } else if (compounding === 1) {
93
- total = initialDeposit * Math.pow(1 + interestRate, months / 12) + monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months / 12) - 1);
94
- } else {
95
- total = initialDeposit;
96
- for (let i = 0; i < months; i++) {
97
- total += monthlyDeposits;
98
- total *= Math.pow(1 + interestRate / compounding, compounding / 12);
99
- }
217
+ total = initialDeposit;
218
+ for (let i = 0; i < months; i++) {
219
+ total += monthlyDeposits;
220
+ total *= Math.pow(1 + interestRate / compounding, compounding / 12);
100
221
  }
101
- setEndingBalance(
102
- total.toLocaleString("en-US", { style: "currency", currency: "USD" })
103
- );
104
222
  }
105
- };
106
- const updateAPR = (value) => {
107
- setAPR(value);
108
- const new_apy = Number.parseFloat(
109
- (100 * ((1 + value / 100 / compounding) ** compounding - 1)).toFixed(2)
110
- );
111
- setAPY(new_apy);
112
- convertInterest();
113
- };
114
- const updateAPY = (value) => {
115
- setAPY(value);
116
- const new_apr = getAPR(value);
117
- setAPR(new_apr);
118
- convertInterest();
119
- };
120
- return /* @__PURE__ */ jsx("section", { className: `${container({ variant: getVariant(variant) })}`, children: /* @__PURE__ */ jsxs("div", { className: `${apy_calculator} containment flex between`, children: [
121
- /* @__PURE__ */ jsxs("div", { className: `${calculator_section}`, children: [
122
- (header || body) && /* @__PURE__ */ jsxs("div", { className: `${section_header}`, children: [
123
- header && /* @__PURE__ */ jsx(
124
- "h2",
125
- {
126
- className: `header_2 ${header_theme({ variant: calculator_variant })}`,
127
- children: header
128
- }
129
- ),
130
- body && /* @__PURE__ */ jsx("div", { className: mt_8, children: body })
131
- ] }),
132
- /* @__PURE__ */ jsxs("form", { id: "calculator_form", className: `${apy_calculator_form}`, children: [
133
- /* @__PURE__ */ jsx("div", { id: "errmsgbox", className: " flex middle", children: errors.length > 0 && /* @__PURE__ */ jsx("ul", { className: pis_0, children: errors.map((error, index) => /* @__PURE__ */ jsx("li", { className: `${errorTag}`, children: error }, index)) }) }),
134
- /* @__PURE__ */ jsxs("div", { className: `${fieldset}`, children: [
135
- /* @__PURE__ */ jsxs("div", { className: `${field_row} ${relative} flex flex_col`, children: [
136
- /* @__PURE__ */ jsx("label", { className: "input_label", htmlFor: "initDeposit", children: "Initial Deposit" }),
137
- /* @__PURE__ */ jsx("label", { className: `${label_symbol} ${cash}`, children: "$" }),
138
- /* @__PURE__ */ jsx(
139
- "input",
140
- {
141
- className: `${prefix_pad} bordered`,
142
- id: "initDeposit",
143
- type: "number",
144
- step: 100,
145
- name: "initDeposit",
146
- value: initialDeposit,
147
- onChange: (e) => setInititalDeposit(parseInt(e.target.value))
148
- }
149
- )
150
- ] }),
151
- /* @__PURE__ */ jsxs("div", { className: `${field_row} ${relative} flex flex_col`, children: [
152
- /* @__PURE__ */ jsx("label", { className: "input_label", htmlFor: "apr", children: "APR (Annual Percentage Rate)" }),
153
- /* @__PURE__ */ jsx("label", { className: `${label_symbol} ${percent}`, children: "%" }),
154
- /* @__PURE__ */ jsx(
155
- "input",
156
- {
157
- className: `${prefix_pad} bordered`,
158
- id: "apr",
159
- type: "number",
160
- step: 0.01,
161
- name: "apr",
162
- value: APR,
163
- onChange: (e) => updateAPR(parseFloat(e.target.value))
164
- }
165
- )
166
- ] }),
167
- /* @__PURE__ */ jsxs("div", { className: `${field_row} ${relative} flex flex_col`, children: [
168
- /* @__PURE__ */ jsx("label", { className: "input_label", htmlFor: "apy", children: "APY (Annual Percentage Yield)" }),
169
- /* @__PURE__ */ jsx("label", { className: `${label_symbol} ${percent}`, children: "%" }),
170
- /* @__PURE__ */ jsx(
171
- "input",
172
- {
173
- className: `${prefix_pad} bordered`,
174
- id: "apy",
175
- type: "number",
176
- step: 0.01,
177
- name: "apy",
178
- value: APY,
179
- onChange: (e) => updateAPY(parseFloat(e.target.value))
180
- }
181
- )
182
- ] }),
183
- /* @__PURE__ */ jsxs("div", { className: `${field_row} ${relative} flex flex_col`, children: [
184
- /* @__PURE__ */ jsx("label", { className: "input_label", htmlFor: "months", children: "Months" }),
185
- /* @__PURE__ */ jsx(
186
- "input",
187
- {
188
- className: `${prefix_pad} bordered`,
189
- id: "months",
190
- min: 1,
191
- maxLength: 4,
192
- type: "number",
193
- name: "months",
194
- value: months,
195
- onChange: (e) => setMonths(parseInt(e.target.value))
196
- }
197
- )
198
- ] }),
199
- /* @__PURE__ */ jsxs("div", { className: `${field_row} ${relative} flex flex_col`, children: [
200
- /* @__PURE__ */ jsx("label", { className: "input_label", htmlFor: "compounding", children: "Compounding" }),
201
- /* @__PURE__ */ jsxs(
202
- "select",
203
- {
204
- className: `${prefix_pad} bordered`,
205
- id: "compounding",
206
- name: "compounding",
207
- value: compounding,
208
- onChange: (e) => setCompounding(Number(e.target.value)),
209
- children: [
210
- /* @__PURE__ */ jsx("option", { value: 360, children: "Daily" }),
211
- /* @__PURE__ */ jsx("option", { value: 12, children: "Monthly" }),
212
- /* @__PURE__ */ jsx("option", { value: 4, children: "Quarterly" }),
213
- /* @__PURE__ */ jsx("option", { value: 2, children: "Semi-Annual" }),
214
- /* @__PURE__ */ jsx("option", { value: 1, children: "Yearly" })
215
- ]
216
- }
217
- )
218
- ] }),
219
- /* @__PURE__ */ jsxs("div", { className: `${field_row} ${relative} flex flex_col`, children: [
220
- /* @__PURE__ */ jsx("label", { className: "input_label", htmlFor: "monthlyDeposits", children: "Monthly Deposits" }),
221
- /* @__PURE__ */ jsx("label", { className: `${label_symbol} ${cash}`, children: "$" }),
222
- /* @__PURE__ */ jsx(
223
- "input",
224
- {
225
- className: `${prefix_pad} bordered`,
226
- id: "monthlyDeposits",
227
- maxLength: 25,
228
- type: "number",
229
- value: monthlyDeposits,
230
- name: "monthlyDeposits",
231
- onChange: (e) => setMonthlyDeposits(parseInt(e.target.value))
232
- }
233
- )
234
- ] }),
235
- /* @__PURE__ */ jsxs("div", { className: `${field_row} ${relative} flex flex_col`, children: [
236
- /* @__PURE__ */ jsx("label", { className: "input_label", htmlFor: "endBal", children: "Ending Balance" }),
237
- /* @__PURE__ */ jsx(
238
- "input",
239
- {
240
- className: `${prefix_pad} bordered`,
241
- id: "endBal",
242
- maxLength: 30,
243
- type: "string",
244
- readOnly: true,
245
- name: "endBal",
246
- value: endingBalance
247
- }
248
- )
249
- ] }),
250
- /* @__PURE__ */ jsx(
251
- "div",
223
+ const deposits = initialDeposit + monthlyDeposits * months;
224
+ const interest = total - deposits;
225
+ setEndingBalanceRaw(total);
226
+ setTotalDepositsRaw(deposits);
227
+ setInterestEarnedRaw(interest > 0 ? interest : 0);
228
+ }, [initialDeposit, APY, months, monthlyDeposits, compounding, getAPR]);
229
+ useEffect(() => {
230
+ calculate();
231
+ }, [calculate]);
232
+ return /* @__PURE__ */ jsxs(
233
+ "section",
234
+ {
235
+ className: `${container({ variant: getVariant(variant) })} ${apy_calculator}`,
236
+ children: [
237
+ (header || body) && /* @__PURE__ */ jsxs("div", { className: `${section_header} containment`, children: [
238
+ header && /* @__PURE__ */ jsx(
239
+ "h1",
252
240
  {
253
- className: `${submit_section} ${span_12} flex center middle push_up_32`,
254
- children: /* @__PURE__ */ jsx(
255
- "input",
256
- {
257
- className: `${button({ color: "primary", size: "medium", rounded: "medium" })} center`,
258
- type: "button",
259
- value: "Calculate",
260
- onClick: handleCalculate
261
- }
262
- )
241
+ className: `header_1 ${header_theme({ variant: calculator_variant })}`,
242
+ children: header
263
243
  }
264
244
  ),
265
- disclosure && /* @__PURE__ */ jsx("div", { className: `${form_disclosure} push_up_24`, children: disclosure })
266
- ] })
267
- ] })
268
- ] }),
269
- /* @__PURE__ */ jsx("div", { className: `${marketing} ${section_container}`, children: marketingTiles && marketingTiles?.map(
270
- ({ id, headline, bodyCopy, callToActionRow }) => /* @__PURE__ */ jsxs(
271
- "div",
272
- {
273
- className: `${container({ variant: getVariant(variant) })} ${marketingTile} rounded bordered`,
274
- children: [
275
- /* @__PURE__ */ jsx("div", { className: `${content} ${bodyContent}`, children: /* @__PURE__ */ jsxs(
276
- "div",
277
- {
278
- className: headerIconBillboard,
279
- style: { textAlign: "left" },
280
- children: [
245
+ body && /* @__PURE__ */ jsx("div", { className: mt_8, children: body })
246
+ ] }),
247
+ /* @__PURE__ */ jsxs(
248
+ "div",
249
+ {
250
+ className: `${apy_calculator} flex between`,
251
+ style: { paddingBlock: "0px" },
252
+ children: [
253
+ /* @__PURE__ */ jsx("div", { className: calculator_section, children: /* @__PURE__ */ jsx("form", { id: "calculator_form", className: apy_calculator_form, children: /* @__PURE__ */ jsxs("div", { className: fieldset, children: [
254
+ /* @__PURE__ */ jsxs(
255
+ Field,
256
+ {
257
+ className: `${field_row} ${relative} flex flex_col`,
258
+ style: { marginTop: 0 },
259
+ children: [
260
+ /* @__PURE__ */ jsx(Label, { className: field_label, children: "Initial Deposit" }),
261
+ /* @__PURE__ */ jsx(
262
+ Input,
263
+ {
264
+ className: `${field_row_input} ${fieldErrors.initialDeposit ? ` ${field_row_input_error}` : ""}`,
265
+ id: "initDeposit",
266
+ type: "text",
267
+ inputMode: "numeric",
268
+ name: "initDeposit",
269
+ value: initialDepositDisplay ? `$${initialDepositDisplay}` : "",
270
+ onChange: handleInitialDepositChange,
271
+ "aria-invalid": !!fieldErrors.initialDeposit,
272
+ "aria-describedby": fieldErrors.initialDeposit ? "initDeposit-error" : void 0
273
+ }
274
+ ),
275
+ fieldErrors.initialDeposit && /* @__PURE__ */ jsx(
276
+ Description,
277
+ {
278
+ as: "span",
279
+ id: "initDeposit-error",
280
+ className: field_error,
281
+ role: "alert",
282
+ children: fieldErrors.initialDeposit
283
+ }
284
+ )
285
+ ]
286
+ }
287
+ ),
288
+ /* @__PURE__ */ jsxs(Field, { className: `${field_row} ${relative} flex flex_col`, children: [
289
+ /* @__PURE__ */ jsx(Label, { className: field_label, children: "APY (Annual Percentage Yield)" }),
281
290
  /* @__PURE__ */ jsx(
282
- "div",
291
+ "span",
283
292
  {
284
- className: `header_3 ${header_theme({ variant: calculator_variant })}`,
285
- children: headline
293
+ className: `${label_symbol} ${percent}`,
294
+ "aria-hidden": "true",
295
+ children: "%"
286
296
  }
287
297
  ),
288
- /* @__PURE__ */ jsx("div", { children: bodyCopy })
289
- ]
290
- }
291
- ) }),
292
- callToActionRow && /* @__PURE__ */ jsx("div", { className: `${buttons} middle`, children: callToActionRow.map(
293
- ({
294
- id: id2,
295
- variant: variant2,
296
- displayText,
297
- targetUrl,
298
- type
299
- }) => type === "Button" ? /* @__PURE__ */ jsx(
300
- Button,
301
- {
302
- targetUrl,
303
- color: getVariant(variant2),
304
- size: "medium",
305
- rounded: "medium",
306
- children: displayText
307
- },
308
- id2
309
- ) : /* @__PURE__ */ jsx(
310
- Chevron,
298
+ /* @__PURE__ */ jsx(
299
+ Input,
300
+ {
301
+ className: `${field_row_input} ${fieldErrors.APY ? ` ${field_row_input_error}` : ""}`,
302
+ id: "apy",
303
+ type: "text",
304
+ inputMode: "numeric",
305
+ name: "apy",
306
+ value: APYDisplay,
307
+ onChange: handleAPYChange,
308
+ onWheel: (e) => e.currentTarget.blur(),
309
+ "aria-invalid": !!fieldErrors.APY,
310
+ "aria-describedby": fieldErrors.APY ? "apy-error" : void 0
311
+ }
312
+ ),
313
+ fieldErrors.APY && /* @__PURE__ */ jsx(
314
+ Description,
315
+ {
316
+ as: "span",
317
+ id: "apy-error",
318
+ className: field_error,
319
+ role: "alert",
320
+ children: fieldErrors.APY
321
+ }
322
+ )
323
+ ] }),
324
+ /* @__PURE__ */ jsxs(Field, { className: `${field_row} ${relative} flex flex_col`, children: [
325
+ /* @__PURE__ */ jsx(Label, { className: field_label, children: "Months" }),
326
+ /* @__PURE__ */ jsx(
327
+ Input,
328
+ {
329
+ className: `${field_row_input}${fieldErrors.months ? ` ${field_row_input_error}` : ""}`,
330
+ id: "months",
331
+ min: 1,
332
+ type: "text",
333
+ inputMode: "numeric",
334
+ name: "months",
335
+ value: monthsDisplay,
336
+ onChange: handleMonthsChange,
337
+ "aria-invalid": !!fieldErrors.months,
338
+ "aria-describedby": fieldErrors.months ? "months-error" : void 0
339
+ }
340
+ ),
341
+ fieldErrors.months && /* @__PURE__ */ jsx(
342
+ Description,
343
+ {
344
+ as: "span",
345
+ id: "months-error",
346
+ className: field_error,
347
+ role: "alert",
348
+ children: fieldErrors.months
349
+ }
350
+ )
351
+ ] }),
352
+ /* @__PURE__ */ jsxs(Field, { className: `${field_row} ${relative} flex flex_col`, children: [
353
+ /* @__PURE__ */ jsx(Label, { className: field_label, htmlFor: "compounding", children: "Compounding" }),
354
+ /* @__PURE__ */ jsxs(
355
+ Listbox,
356
+ {
357
+ value: compoundingOption,
358
+ onChange: setCompoundingOption,
359
+ children: [
360
+ /* @__PURE__ */ jsxs(
361
+ ListboxButton,
362
+ {
363
+ id: "compounding",
364
+ className: `${listbox_button} ${field_row_input}`,
365
+ children: [
366
+ /* @__PURE__ */ jsx("span", { children: compoundingOption.label }),
367
+ /* @__PURE__ */ jsx(
368
+ "svg",
369
+ {
370
+ className: chevron_icon,
371
+ xmlns: "http://www.w3.org/2000/svg",
372
+ viewBox: "0 0 20 20",
373
+ fill: "currentColor",
374
+ "aria-hidden": "true",
375
+ children: /* @__PURE__ */ jsx(
376
+ "path",
377
+ {
378
+ fillRule: "evenodd",
379
+ d: "M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z",
380
+ clipRule: "evenodd"
381
+ }
382
+ )
383
+ }
384
+ )
385
+ ]
386
+ }
387
+ ),
388
+ /* @__PURE__ */ jsx(
389
+ ListboxOptions,
390
+ {
391
+ anchor: "bottom start",
392
+ className: listbox_options,
393
+ children: COMPOUNDING_OPTIONS.map((opt) => /* @__PURE__ */ jsx(
394
+ ListboxOption,
395
+ {
396
+ value: opt,
397
+ className: listbox_option,
398
+ children: opt.label
399
+ },
400
+ opt.value
401
+ ))
402
+ }
403
+ )
404
+ ]
405
+ }
406
+ )
407
+ ] }),
408
+ /* @__PURE__ */ jsxs(Field, { className: `${field_row} ${relative} flex flex_col`, children: [
409
+ /* @__PURE__ */ jsxs(Label, { className: field_label, children: [
410
+ "Monthly Deposits",
411
+ " ",
412
+ /* @__PURE__ */ jsx("span", { className: optional_badge, children: "(optional)" })
413
+ ] }),
414
+ /* @__PURE__ */ jsx(
415
+ Input,
416
+ {
417
+ className: `${field_row_input} ${fieldErrors.monthlyDeposits ? ` ${field_row_input_error}` : ""}`,
418
+ id: "monthlyDeposits",
419
+ type: "text",
420
+ inputMode: "numeric",
421
+ name: "monthlyDeposits",
422
+ value: monthlyDepositsDisplay ? `$${monthlyDepositsDisplay}` : "",
423
+ placeholder: "0",
424
+ onChange: handleMonthlyDepositsChange,
425
+ "aria-invalid": !!fieldErrors.monthlyDeposits,
426
+ "aria-describedby": fieldErrors.monthlyDeposits ? "monthlyDeposits-error" : void 0
427
+ }
428
+ ),
429
+ fieldErrors.monthlyDeposits && /* @__PURE__ */ jsx(
430
+ Description,
431
+ {
432
+ as: "span",
433
+ id: "monthlyDeposits-error",
434
+ className: field_error,
435
+ role: "alert",
436
+ children: fieldErrors.monthlyDeposits
437
+ }
438
+ )
439
+ ] }),
440
+ disclosure && /* @__PURE__ */ jsx("div", { className: `${form_disclosure} push_up_24`, children: disclosure })
441
+ ] }) }) }),
442
+ /* @__PURE__ */ jsx("div", { className: results_wrapper, children: /* @__PURE__ */ jsxs("div", { className: result_card, children: [
443
+ /* @__PURE__ */ jsxs(
444
+ "div",
445
+ {
446
+ className: result_section,
447
+ "aria-live": "polite",
448
+ "aria-atomic": "true",
449
+ "aria-label": `Interest earned: ${interestEarnedDisplay}`,
450
+ children: [
451
+ /* @__PURE__ */ jsx(
452
+ Image,
453
+ {
454
+ src: "https://www.axos.com/images/5IyqVlv7El9K7hAX0vIGWG/axb-25th-growth-lg.png",
455
+ alt: "",
456
+ height: 173,
457
+ width: 202,
458
+ className: `img_fluid ${result_image}`,
459
+ priority: true
460
+ }
461
+ ),
462
+ /* @__PURE__ */ jsx(
463
+ "div",
464
+ {
465
+ className: `${result_value} ${interestScrambling ? scrambling : ""}`,
466
+ children: interestEarnedDisplay
467
+ }
468
+ ),
469
+ /* @__PURE__ */ jsx("div", { className: result_value_label, children: "Interest Earned" })
470
+ ]
471
+ }
472
+ ),
473
+ /* @__PURE__ */ jsxs("div", { "aria-live": "polite", "aria-atomic": "true", children: [
474
+ /* @__PURE__ */ jsxs("div", { className: result_item, children: [
475
+ /* @__PURE__ */ jsx("span", { className: result_item_label, children: "Ending Balance:" }),
476
+ /* @__PURE__ */ jsx(
477
+ "span",
478
+ {
479
+ className: `${result_item_value} ${balanceScrambling ? scrambling : ""}`,
480
+ children: endingBalanceDisplay
481
+ }
482
+ )
483
+ ] }),
484
+ /* @__PURE__ */ jsxs("div", { className: result_item, children: [
485
+ /* @__PURE__ */ jsx("span", { className: result_item_label, children: "Total Deposits:" }),
486
+ /* @__PURE__ */ jsx(
487
+ "span",
488
+ {
489
+ className: `${result_item_value} ${depositsScrambling ? scrambling : ""}`,
490
+ children: totalDepositsDisplay
491
+ }
492
+ )
493
+ ] })
494
+ ] })
495
+ ] }) })
496
+ ]
497
+ }
498
+ ),
499
+ /* @__PURE__ */ jsx("div", { id: "definitions", className: "push_up_24", children: /* @__PURE__ */ jsx(
500
+ "div",
501
+ {
502
+ className: `${definitions_accordion} ${apy_calculator}`,
503
+ "aria-label": "Calculator definitions",
504
+ style: { paddingBlock: "0px" },
505
+ children: /* @__PURE__ */ jsx(Disclosure, { children: ({ open }) => /* @__PURE__ */ jsxs(Fragment, { children: [
506
+ /* @__PURE__ */ jsxs(DisclosureButton, { className: definitions_button, children: [
507
+ /* @__PURE__ */ jsx(
508
+ "h2",
509
+ {
510
+ className: definitions_h2({ variant: getVariant(variant) }),
511
+ children: "Calculator definitions"
512
+ }
513
+ ),
514
+ /* @__PURE__ */ jsx(
515
+ "svg",
516
+ {
517
+ xmlns: "http://www.w3.org/2000/svg",
518
+ width: "20",
519
+ height: "20",
520
+ viewBox: "0 0 20 20",
521
+ fill: "none",
522
+ "aria-hidden": "true",
523
+ children: /* @__PURE__ */ jsx(
524
+ "path",
525
+ {
526
+ d: "M10 1.25C14.8325 1.25 18.75 5.16751 18.75 10C18.75 14.8325 14.8325 18.75 10 18.75C5.16751 18.75 1.25 14.8325 1.25 10C1.25 5.16751 5.16751 1.25 10 1.25ZM10 2.5C5.85786 2.5 2.5 5.85786 2.5 10C2.5 14.1421 5.85786 17.5 10 17.5C14.1421 17.5 17.5 14.1421 17.5 10C17.5 5.85786 14.1421 2.5 10 2.5ZM10.625 8.75V14.375H9.375V8.75H10.625ZM10 5.625C10.5178 5.625 10.9375 6.04473 10.9375 6.5625C10.9375 7.08027 10.5178 7.5 10 7.5C9.48223 7.5 9.0625 7.08027 9.0625 6.5625C9.0625 6.04473 9.48223 5.625 10 5.625Z",
527
+ fill: "#435164"
528
+ }
529
+ )
530
+ }
531
+ )
532
+ ] }),
533
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: open && /* @__PURE__ */ jsx(
534
+ DisclosurePanel,
311
535
  {
312
- targetUrl,
313
- variant: getVariant(variant2),
314
- children: displayText
315
- },
316
- id2
317
- )
318
- ) })
319
- ]
320
- },
321
- id
322
- )
323
- ) })
324
- ] }) });
536
+ static: true,
537
+ as: motion.div,
538
+ initial: { opacity: 0, height: 0, y: -6 },
539
+ animate: { opacity: 1, height: "auto", y: 0 },
540
+ exit: { opacity: 0, height: 0, y: -4 },
541
+ style: { overflow: "hidden" },
542
+ children: /* @__PURE__ */ jsxs(
543
+ "div",
544
+ {
545
+ className: `${definitions_columns} ${definitions_panel}`,
546
+ children: [
547
+ /* @__PURE__ */ jsx("div", { className: definitions_column, children: leftDefinitions.map((definition, index) => /* @__PURE__ */ jsxs(
548
+ motion.div,
549
+ {
550
+ className: definitions_item,
551
+ initial: { opacity: 0, y: 8 },
552
+ animate: { opacity: 1, y: 0 },
553
+ exit: { opacity: 0, y: 6 },
554
+ transition: {
555
+ ...DEFINITIONS_ITEM_TRANSITION,
556
+ delay: index * 0.045
557
+ },
558
+ children: [
559
+ /* @__PURE__ */ jsx("h3", { className: definitions_term, children: definition.term }),
560
+ /* @__PURE__ */ jsx("p", { className: definitions_text, children: definition.description })
561
+ ]
562
+ },
563
+ definition.term
564
+ )) }),
565
+ /* @__PURE__ */ jsx("div", { className: definitions_column, children: rightDefinitions.map((definition, index) => /* @__PURE__ */ jsxs(
566
+ motion.div,
567
+ {
568
+ className: definitions_item,
569
+ initial: { opacity: 0, y: 8 },
570
+ animate: { opacity: 1, y: 0 },
571
+ exit: { opacity: 0, y: 6 },
572
+ transition: {
573
+ ...DEFINITIONS_ITEM_TRANSITION,
574
+ delay: (leftDefinitions.length + index) * 0.045
575
+ },
576
+ children: [
577
+ /* @__PURE__ */ jsx("h3", { className: definitions_term, children: definition.term }),
578
+ /* @__PURE__ */ jsx("p", { className: definitions_text, children: definition.description })
579
+ ]
580
+ },
581
+ definition.term
582
+ )) })
583
+ ]
584
+ }
585
+ )
586
+ }
587
+ ) })
588
+ ] }) })
589
+ }
590
+ ) })
591
+ ]
592
+ }
593
+ );
325
594
  };
326
595
  export {
327
596
  ApyCalculator