@axos-web-dev/shared-components 2.0.0-dev.18 → 2.0.0-dev.18-apy.1

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 (89) hide show
  1. package/README.md +111 -111
  2. package/dist/ATMLocator/ATMLocator.js +1 -1
  3. package/dist/Auth/ErrorAlert.js +1 -1
  4. package/dist/Avatar/Avatar.module.js +7 -7
  5. package/dist/Blockquote/Blockquote.module.js +3 -3
  6. package/dist/Button/Button.js +1 -1
  7. package/dist/Calculators/ApyCalculator/ApyCalculator.css.d.ts +20 -3
  8. package/dist/Calculators/ApyCalculator/ApyCalculator.css.js +51 -16
  9. package/dist/Calculators/ApyCalculator/index.js +413 -294
  10. package/dist/Calculators/MarginTradingCalculator/index.js +1 -1
  11. package/dist/Carousel/index.js +2 -2
  12. package/dist/Chatbot/AnimatedGradientBorder.css.d.ts +2 -0
  13. package/dist/Chatbot/AnimatedGradientBorder.css.js +6 -2
  14. package/dist/Chatbot/Bubble.d.ts +1 -1
  15. package/dist/Chatbot/Bubble.js +92 -102
  16. package/dist/Chatbot/Chat.js +1 -2
  17. package/dist/Chatbot/ChatWindow.css.js +35 -35
  18. package/dist/Chatbot/Chatbot.d.ts +1 -1
  19. package/dist/Chatbot/Chatbot.js +1 -5
  20. package/dist/Chatbot/useHeadlessChat.js +1 -2
  21. package/dist/Chevron/index.js +1 -1
  22. package/dist/Comparison/Comparison.js +1 -1
  23. package/dist/ExecutiveBio/ExecutiveBio.js +1 -1
  24. package/dist/FaqAccordion/index.js +1 -1
  25. package/dist/FdicCallout/FdicCallout.module.js +2 -2
  26. package/dist/FooterSiteMap/AxosBank/FooterSiteMap.js +1 -1
  27. package/dist/Forms/ApplyNow.js +2 -2
  28. package/dist/Forms/BoatMooringLocation.d.ts +4 -1
  29. package/dist/Forms/BoatMooringLocation.js +89 -8
  30. package/dist/Forms/ContactUsBusiness.js +1 -1
  31. package/dist/Forms/ContactUsBusinessNameEmail.js +1 -1
  32. package/dist/Forms/ContactUsNMLSId.js +1 -1
  33. package/dist/Forms/CpraRequest.js +1 -1
  34. package/dist/Forms/CraPublicFile.js +1 -1
  35. package/dist/Forms/EmailOnly.js +1 -1
  36. package/dist/Forms/MortgageRate/MortgageRateForm.js +2 -2
  37. package/dist/Forms/MortgageRate/MortgageRateWatch.js +2 -2
  38. package/dist/Forms/MortgageWarehouseLending.js +1 -1
  39. package/dist/Forms/SuccesForm.js +2 -2
  40. package/dist/Hyperlink/index.js +1 -1
  41. package/dist/ImageLink/ImageLink.js +2 -2
  42. package/dist/ImageLink/ImageLinkSet.js +1 -1
  43. package/dist/ImageLink/index.js +1 -1
  44. package/dist/Insight/Featured/CategorySelector.js +2 -2
  45. package/dist/Insight/Featured/Featured.js +2 -2
  46. package/dist/Insight/Featured/Header.js +2 -2
  47. package/dist/Interstitial/Interstitial.module.js +10 -10
  48. package/dist/Modal/Modal.js +1 -1
  49. package/dist/NavigationMenu/AxosALTS/NavBar.module.js +23 -23
  50. package/dist/NavigationMenu/AxosALTS/index.js +1 -1
  51. package/dist/NavigationMenu/AxosAdvisor/NavBar.module.js +52 -52
  52. package/dist/NavigationMenu/AxosAdvisorServices/NavBar.module.js +53 -53
  53. package/dist/NavigationMenu/AxosBank/MobileMenu/MobileMenu.js +1 -1
  54. package/dist/NavigationMenu/AxosBank/MobileMenu/MobileMenu.module.js +27 -27
  55. package/dist/NavigationMenu/AxosBank/NavBar.module.js +39 -39
  56. package/dist/NavigationMenu/AxosBank/SubNavBar.js +1 -1
  57. package/dist/NavigationMenu/AxosBank/index.js +1 -1
  58. package/dist/NavigationMenu/AxosClearing/NavBar.module.js +37 -37
  59. package/dist/NavigationMenu/AxosFiduciary/NavBar.module.js +41 -41
  60. package/dist/NavigationMenu/LaVictoire/NavBar.module.js +37 -37
  61. package/dist/NavigationMenu/LaVictoire/index.js +1 -1
  62. package/dist/NavigationMenu/Navbar.js +1 -1
  63. package/dist/NavigationMenu/SignInNavButton.js +1 -1
  64. package/dist/SetContainer/SetContainer.js +2 -2
  65. package/dist/SocialMediaBar/iconsRepository.js +1 -1
  66. package/dist/VideoTile/VideoTile.js +1 -1
  67. package/dist/VideoWrapper/index.js +1 -1
  68. package/dist/WalnutIframe/wrapper.module.js +3 -3
  69. package/dist/assets/Avatar/Avatar.css +59 -59
  70. package/dist/assets/Blockquote/Blockquote.css +72 -72
  71. package/dist/assets/Calculators/ApyCalculator/ApyCalculator.css +230 -56
  72. package/dist/assets/Chatbot/AnimatedGradientBorder.css +39 -8
  73. package/dist/assets/Chatbot/ChatWindow.css +77 -67
  74. package/dist/assets/FdicCallout/FdicCallout.css +48 -48
  75. package/dist/assets/Interstitial/Interstitial.css +142 -142
  76. package/dist/assets/NavigationMenu/AxosALTS/NavBar.css +264 -264
  77. package/dist/assets/NavigationMenu/AxosAdvisor/NavBar.css +609 -609
  78. package/dist/assets/NavigationMenu/AxosAdvisorServices/NavBar.css +630 -630
  79. package/dist/assets/NavigationMenu/AxosBank/MobileMenu/MobileMenu.css +353 -353
  80. package/dist/assets/NavigationMenu/AxosBank/NavBar.css +88 -88
  81. package/dist/assets/NavigationMenu/AxosClearing/NavBar.css +484 -484
  82. package/dist/assets/NavigationMenu/AxosFiduciary/NavBar.css +427 -427
  83. package/dist/assets/NavigationMenu/LaVictoire/NavBar.css +429 -429
  84. package/dist/assets/WalnutIframe/wrapper.css +48 -48
  85. package/dist/assets/themes/axos.css +1 -1
  86. package/dist/assets/themes/ufb.css +1 -1
  87. package/dist/assets/utils/optimizeImage/optimizeImage.css +47 -47
  88. package/dist/utils/optimizeImage/optimizeImage.module.js +3 -3
  89. package/package.json +2 -2
@@ -1,327 +1,446 @@
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 } 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 } from "@headlessui/react";
7
+ import { useState, useCallback, useEffect, useRef } from "react";
8
+ 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_value, scrambling, result_value_label, result_item, result_item_label, result_item_value, container } from "./ApyCalculator.css.js";
9
+ const COMPOUNDING_OPTIONS = [
10
+ { value: 360, label: "Daily" },
11
+ { value: 12, label: "Monthly" },
12
+ { value: 4, label: "Quarterly" },
13
+ { value: 2, label: "Semi-Annual" },
14
+ { value: 1, label: "Yearly" }
15
+ ];
16
+ const SCRAMBLE_CHARS = "0123456789";
17
+ function scrambleCurrency(target, progress) {
18
+ const formatted = target.toLocaleString("en-US", {
19
+ style: "currency",
20
+ currency: "USD"
21
+ });
22
+ return formatted.split("").map((char) => {
23
+ if (char === "$" || char === "," || char === "." || char === "-") {
24
+ return char;
25
+ }
26
+ if (/\d/.test(char)) {
27
+ return Math.random() > progress ? SCRAMBLE_CHARS[Math.floor(Math.random() * SCRAMBLE_CHARS.length)] : char;
28
+ }
29
+ return char;
30
+ }).join("");
31
+ }
32
+ function useScramble(targetValue, duration = 700) {
33
+ const [display, setDisplay] = useState(
34
+ () => targetValue.toLocaleString("en-US", { style: "currency", currency: "USD" })
35
+ );
36
+ const [isScrambling, setIsScrambling] = useState(false);
37
+ const rafRef = useRef(null);
38
+ const startRef = useRef(0);
39
+ const prevTarget = useRef(targetValue);
40
+ useEffect(() => {
41
+ if (prevTarget.current === targetValue) return;
42
+ prevTarget.current = targetValue;
43
+ setIsScrambling(true);
44
+ startRef.current = Date.now();
45
+ const tick = () => {
46
+ const elapsed = Date.now() - startRef.current;
47
+ const progress = Math.min(elapsed / duration, 1);
48
+ if (progress < 1) {
49
+ setDisplay(scrambleCurrency(targetValue, progress));
50
+ rafRef.current = setTimeout(tick, 40);
51
+ } else {
52
+ setDisplay(
53
+ targetValue.toLocaleString("en-US", {
54
+ style: "currency",
55
+ currency: "USD"
56
+ })
57
+ );
58
+ setIsScrambling(false);
59
+ }
60
+ };
61
+ if (rafRef.current) clearTimeout(rafRef.current);
62
+ rafRef.current = setTimeout(tick, 40);
63
+ return () => {
64
+ if (rafRef.current) clearTimeout(rafRef.current);
65
+ };
66
+ }, [targetValue, duration]);
67
+ return { display, isScrambling };
68
+ }
69
+ function formatWithCommas(value) {
70
+ return value.toLocaleString("en-US");
71
+ }
72
+ function parseCurrencyInput(raw) {
73
+ const digits = raw.replace(/[^0-9]/g, "");
74
+ return digits === "" ? 0 : parseInt(digits, 10);
75
+ }
12
76
  const ApyCalculator = ({
13
77
  header,
14
78
  body,
15
- marketingTiles,
16
79
  disclosure,
17
80
  variant
18
81
  }) => {
19
82
  const { domains } = useGlobalContext();
20
83
  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);
84
+ const [compoundingOption, setCompoundingOption] = useState(
85
+ COMPOUNDING_OPTIONS[0]
86
+ );
87
+ const compounding = compoundingOption.value;
88
+ const getAPR = useCallback(
89
+ (apy) => {
90
+ return Number.parseFloat(
91
+ (((1 + apy / 100) ** (1 / compounding) - 1) * compounding * 100).toFixed(2)
92
+ );
93
+ },
94
+ [compounding]
95
+ );
96
+ const AXOS_ONE_APY = Number(domains.AXOS_ONE_APY) || 4.21;
97
+ const [initialDeposit, setInitialDeposit] = useState(1e4);
98
+ const [initialDepositDisplay, setInitialDepositDisplay] = useState(
99
+ formatWithCommas(1e4)
100
+ );
33
101
  const [APY, setAPY] = useState(AXOS_ONE_APY);
34
102
  const [months, setMonths] = useState(12);
35
103
  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;
104
+ const [monthlyDepositsDisplay, setMonthlyDepositsDisplay] = useState(
105
+ formatWithCommas(100)
106
+ );
107
+ const [fieldErrors, setFieldErrors] = useState({});
108
+ const handleInitialDepositChange = (e) => {
109
+ const raw = e.target.value;
110
+ const num = parseCurrencyInput(raw);
111
+ setInitialDeposit(num);
112
+ setInitialDepositDisplay(num > 0 ? formatWithCommas(num) : "");
52
113
  };
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.");
114
+ const handleMonthlyDepositsChange = (e) => {
115
+ const raw = e.target.value;
116
+ if (raw === "") {
117
+ setMonthlyDeposits(0);
118
+ setMonthlyDepositsDisplay("");
119
+ return;
65
120
  }
66
- if (!isValidNumber(months)) {
67
- newErrors.push("Months must be a positive number.");
121
+ const num = parseCurrencyInput(raw);
122
+ setMonthlyDeposits(num);
123
+ setMonthlyDepositsDisplay(formatWithCommas(num));
124
+ };
125
+ const [endingBalanceRaw, setEndingBalanceRaw] = useState(0);
126
+ const [totalDepositsRaw, setTotalDepositsRaw] = useState(0);
127
+ const [interestEarnedRaw, setInterestEarnedRaw] = useState(0);
128
+ const { display: endingBalanceDisplay, isScrambling: balanceScrambling } = useScramble(endingBalanceRaw);
129
+ const { display: totalDepositsDisplay, isScrambling: depositsScrambling } = useScramble(totalDepositsRaw);
130
+ const { display: interestEarnedDisplay, isScrambling: interestScrambling } = useScramble(interestEarnedRaw);
131
+ const calculate = useCallback(() => {
132
+ const newErrors = {};
133
+ if (!initialDeposit || initialDeposit <= 0 || Number.isNaN(initialDeposit)) {
134
+ newErrors.initialDeposit = "Please enter an amount greater than 0.";
68
135
  }
69
- if (!isValidNumber(monthlyDeposits)) {
70
- newErrors.push("Monthly Deposits must be a positive number.");
136
+ if (!months || months < 1 || Number.isNaN(months)) {
137
+ newErrors.months = "Please enter a term of at least 1 month.";
71
138
  }
72
- if (!isValidAPY(APY)) {
73
- newErrors.push("APY must be a positive number.");
139
+ if (monthlyDeposits < 0 || Number.isNaN(monthlyDeposits)) {
140
+ newErrors.monthlyDeposits = "Monthly deposit amount cannot be negative.";
74
141
  }
75
- if (!isValidAPY(APR)) {
76
- newErrors.push("APR must be a positive number.");
142
+ if (!APY || APY <= 0 || Number.isNaN(APY)) {
143
+ newErrors.APY = "Please enter a valid APY greater than 0.";
77
144
  }
78
- if (newErrors.length > 0) {
79
- setErrors(newErrors);
145
+ setFieldErrors(newErrors);
146
+ if (Object.keys(newErrors).length > 0) return;
147
+ const APR = getAPR(APY);
148
+ const interestRate = 0.01 * APR;
149
+ let total;
150
+ if (compounding === 360) {
151
+ total = initialDeposit * Math.pow(1 + interestRate / compounding, 30 * months) + (monthlyDeposits > 0 ? monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, 30 * months) - 1) : 0);
152
+ } else if (compounding === 12) {
153
+ total = initialDeposit * Math.pow(1 + interestRate / compounding, months) + (monthlyDeposits > 0 ? monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months) - 1) : 0);
154
+ } else if (compounding === 4) {
155
+ total = initialDeposit * Math.pow(1 + interestRate / compounding, months / 3) + (monthlyDeposits > 0 ? monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months / 3) - 1) : 0);
156
+ } else if (compounding === 2) {
157
+ total = initialDeposit * Math.pow(1 + interestRate / compounding, months / 6) + (monthlyDeposits > 0 ? monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months / 6) - 1) : 0);
158
+ } else if (compounding === 1) {
159
+ total = initialDeposit * Math.pow(1 + interestRate, months / 12) + (monthlyDeposits > 0 ? monthlyDeposits / (interestRate / 12) * (Math.pow(1 + interestRate / compounding, months / 12) - 1) : 0);
80
160
  } 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
- }
161
+ total = initialDeposit;
162
+ for (let i = 0; i < months; i++) {
163
+ total += monthlyDeposits;
164
+ total *= Math.pow(1 + interestRate / compounding, compounding / 12);
100
165
  }
101
- setEndingBalance(
102
- total.toLocaleString("en-US", { style: "currency", currency: "USD" })
103
- );
104
166
  }
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",
167
+ const deposits = initialDeposit + monthlyDeposits * months;
168
+ const interest = total - deposits;
169
+ setEndingBalanceRaw(total);
170
+ setTotalDepositsRaw(deposits);
171
+ setInterestEarnedRaw(interest > 0 ? interest : 0);
172
+ }, [initialDeposit, APY, months, monthlyDeposits, compounding, getAPR]);
173
+ useEffect(() => {
174
+ calculate();
175
+ }, [calculate]);
176
+ return /* @__PURE__ */ jsxs(
177
+ "section",
178
+ {
179
+ className: `${container({ variant: getVariant(variant) })} ${apy_calculator}`,
180
+ children: [
181
+ (header || body) && /* @__PURE__ */ jsxs("div", { className: `${section_header} containment`, children: [
182
+ header && /* @__PURE__ */ jsx(
183
+ "h1",
252
184
  {
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
- )
185
+ className: `header_1 ${header_theme({ variant: calculator_variant })}`,
186
+ children: header
263
187
  }
264
188
  ),
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: [
189
+ body && /* @__PURE__ */ jsx("div", { className: mt_8, children: body })
190
+ ] }),
191
+ /* @__PURE__ */ jsxs(
192
+ "div",
193
+ {
194
+ className: `${apy_calculator} containment flex between gap_24`,
195
+ style: { paddingBlock: "0px" },
196
+ children: [
197
+ /* @__PURE__ */ jsx("div", { className: `${calculator_section}`, children: /* @__PURE__ */ jsx("form", { id: "calculator_form", className: `${apy_calculator_form}`, children: /* @__PURE__ */ jsxs("div", { className: `${fieldset}`, children: [
198
+ /* @__PURE__ */ jsxs(
199
+ Field,
200
+ {
201
+ className: `${field_row} ${relative} flex flex_col`,
202
+ style: { marginTop: 0 },
203
+ children: [
204
+ /* @__PURE__ */ jsx(Label, { className: field_label, children: "Initial Deposit" }),
205
+ /* @__PURE__ */ jsx(
206
+ Input,
207
+ {
208
+ className: `${field_row_input} ${fieldErrors.initialDeposit ? ` ${field_row_input_error}` : ""}`,
209
+ id: "initDeposit",
210
+ type: "text",
211
+ inputMode: "numeric",
212
+ name: "initDeposit",
213
+ value: initialDepositDisplay ? `$${initialDepositDisplay}` : "",
214
+ onChange: handleInitialDepositChange,
215
+ "aria-invalid": !!fieldErrors.initialDeposit,
216
+ "aria-describedby": fieldErrors.initialDeposit ? "initDeposit-error" : void 0
217
+ }
218
+ ),
219
+ fieldErrors.initialDeposit && /* @__PURE__ */ jsx(
220
+ Description,
221
+ {
222
+ as: "span",
223
+ id: "initDeposit-error",
224
+ className: field_error,
225
+ role: "alert",
226
+ children: fieldErrors.initialDeposit
227
+ }
228
+ )
229
+ ]
230
+ }
231
+ ),
232
+ /* @__PURE__ */ jsxs(Field, { className: `${field_row} ${relative} flex flex_col`, children: [
233
+ /* @__PURE__ */ jsx(Label, { className: field_label, children: "APY (Annual Percentage Yield)" }),
281
234
  /* @__PURE__ */ jsx(
282
- "div",
235
+ "span",
283
236
  {
284
- className: `header_3 ${header_theme({ variant: calculator_variant })}`,
285
- children: headline
237
+ className: `${label_symbol} ${percent}`,
238
+ "aria-hidden": "true",
239
+ children: "%"
286
240
  }
287
241
  ),
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,
311
- {
312
- targetUrl,
313
- variant: getVariant(variant2),
314
- children: displayText
315
- },
316
- id2
317
- )
318
- ) })
319
- ]
320
- },
321
- id
322
- )
323
- ) })
324
- ] }) });
242
+ /* @__PURE__ */ jsx(
243
+ Input,
244
+ {
245
+ className: `${field_row_input} ${fieldErrors.APY ? ` ${field_row_input_error}` : ""}`,
246
+ id: "apy",
247
+ type: "number",
248
+ name: "apy",
249
+ min: 0,
250
+ value: APY,
251
+ onChange: (e) => setAPY(parseFloat(e.target.value)),
252
+ "aria-invalid": !!fieldErrors.APY,
253
+ "aria-describedby": fieldErrors.APY ? "apy-error" : void 0
254
+ }
255
+ ),
256
+ fieldErrors.APY && /* @__PURE__ */ jsx(
257
+ Description,
258
+ {
259
+ as: "span",
260
+ id: "apy-error",
261
+ className: field_error,
262
+ role: "alert",
263
+ children: fieldErrors.APY
264
+ }
265
+ )
266
+ ] }),
267
+ /* @__PURE__ */ jsxs(Field, { className: `${field_row} ${relative} flex flex_col`, children: [
268
+ /* @__PURE__ */ jsx(Label, { className: field_label, children: "Months" }),
269
+ /* @__PURE__ */ jsx(
270
+ Input,
271
+ {
272
+ className: `${field_row_input}${fieldErrors.months ? ` ${field_row_input_error}` : ""}`,
273
+ id: "months",
274
+ min: 1,
275
+ type: "number",
276
+ name: "months",
277
+ value: months,
278
+ onChange: (e) => setMonths(parseInt(e.target.value)),
279
+ "aria-invalid": !!fieldErrors.months,
280
+ "aria-describedby": fieldErrors.months ? "months-error" : void 0
281
+ }
282
+ ),
283
+ fieldErrors.months && /* @__PURE__ */ jsx(
284
+ Description,
285
+ {
286
+ as: "span",
287
+ id: "months-error",
288
+ className: field_error,
289
+ role: "alert",
290
+ children: fieldErrors.months
291
+ }
292
+ )
293
+ ] }),
294
+ /* @__PURE__ */ jsxs(Field, { className: `${field_row} ${relative} flex flex_col`, children: [
295
+ /* @__PURE__ */ jsx(Label, { className: field_label, htmlFor: "compounding", children: "Compounding" }),
296
+ /* @__PURE__ */ jsxs(
297
+ Listbox,
298
+ {
299
+ value: compoundingOption,
300
+ onChange: setCompoundingOption,
301
+ children: [
302
+ /* @__PURE__ */ jsxs(
303
+ ListboxButton,
304
+ {
305
+ id: "compounding",
306
+ className: `${listbox_button} ${field_row_input}`,
307
+ children: [
308
+ /* @__PURE__ */ jsx("span", { children: compoundingOption.label }),
309
+ /* @__PURE__ */ jsx(
310
+ "svg",
311
+ {
312
+ className: chevron_icon,
313
+ xmlns: "http://www.w3.org/2000/svg",
314
+ viewBox: "0 0 20 20",
315
+ fill: "currentColor",
316
+ "aria-hidden": "true",
317
+ children: /* @__PURE__ */ jsx(
318
+ "path",
319
+ {
320
+ fillRule: "evenodd",
321
+ 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",
322
+ clipRule: "evenodd"
323
+ }
324
+ )
325
+ }
326
+ )
327
+ ]
328
+ }
329
+ ),
330
+ /* @__PURE__ */ jsx(
331
+ ListboxOptions,
332
+ {
333
+ anchor: "bottom start",
334
+ className: listbox_options,
335
+ children: COMPOUNDING_OPTIONS.map((opt) => /* @__PURE__ */ jsx(
336
+ ListboxOption,
337
+ {
338
+ value: opt,
339
+ className: listbox_option,
340
+ children: opt.label
341
+ },
342
+ opt.value
343
+ ))
344
+ }
345
+ )
346
+ ]
347
+ }
348
+ )
349
+ ] }),
350
+ /* @__PURE__ */ jsxs(Field, { className: `${field_row} ${relative} flex flex_col`, children: [
351
+ /* @__PURE__ */ jsxs(Label, { className: field_label, children: [
352
+ "Monthly Deposits",
353
+ " ",
354
+ /* @__PURE__ */ jsx("span", { className: optional_badge, children: "(optional)" })
355
+ ] }),
356
+ /* @__PURE__ */ jsx(
357
+ Input,
358
+ {
359
+ className: `${field_row_input} ${fieldErrors.monthlyDeposits ? ` ${field_row_input_error}` : ""}`,
360
+ id: "monthlyDeposits",
361
+ type: "text",
362
+ inputMode: "numeric",
363
+ name: "monthlyDeposits",
364
+ value: monthlyDepositsDisplay ? `$${monthlyDepositsDisplay}` : "",
365
+ placeholder: "0",
366
+ onChange: handleMonthlyDepositsChange,
367
+ "aria-invalid": !!fieldErrors.monthlyDeposits,
368
+ "aria-describedby": fieldErrors.monthlyDeposits ? "monthlyDeposits-error" : void 0
369
+ }
370
+ ),
371
+ fieldErrors.monthlyDeposits && /* @__PURE__ */ jsx(
372
+ Description,
373
+ {
374
+ as: "span",
375
+ id: "monthlyDeposits-error",
376
+ className: field_error,
377
+ role: "alert",
378
+ children: fieldErrors.monthlyDeposits
379
+ }
380
+ )
381
+ ] }),
382
+ disclosure && /* @__PURE__ */ jsx("div", { className: `${form_disclosure} push_up_24`, children: disclosure })
383
+ ] }) }) }),
384
+ /* @__PURE__ */ jsx("div", { className: results_wrapper, children: /* @__PURE__ */ jsxs("div", { className: result_card, children: [
385
+ /* @__PURE__ */ jsxs(
386
+ "div",
387
+ {
388
+ className: result_section,
389
+ "aria-live": "polite",
390
+ "aria-atomic": "true",
391
+ "aria-label": `Interest earned: ${interestEarnedDisplay}`,
392
+ children: [
393
+ /* @__PURE__ */ jsx(
394
+ Image,
395
+ {
396
+ src: "https://www.axos.com/images/5IyqVlv7El9K7hAX0vIGWG/axb-25th-growth-lg.png",
397
+ alt: "",
398
+ height: 135,
399
+ width: 165,
400
+ className: "img_fluid",
401
+ priority: true
402
+ }
403
+ ),
404
+ /* @__PURE__ */ jsx(
405
+ "div",
406
+ {
407
+ className: `${result_value} ${interestScrambling ? scrambling : ""}`,
408
+ children: interestEarnedDisplay
409
+ }
410
+ ),
411
+ /* @__PURE__ */ jsx("div", { className: result_value_label, children: "Interest Earned" })
412
+ ]
413
+ }
414
+ ),
415
+ /* @__PURE__ */ jsxs("div", { "aria-live": "polite", "aria-atomic": "true", children: [
416
+ /* @__PURE__ */ jsxs("div", { className: result_item, children: [
417
+ /* @__PURE__ */ jsx("span", { className: result_item_label, children: "Ending Balance" }),
418
+ /* @__PURE__ */ jsx(
419
+ "span",
420
+ {
421
+ className: `${result_item_value} ${balanceScrambling ? scrambling : ""}`,
422
+ children: endingBalanceDisplay
423
+ }
424
+ )
425
+ ] }),
426
+ /* @__PURE__ */ jsxs("div", { className: result_item, children: [
427
+ /* @__PURE__ */ jsx("span", { className: result_item_label, children: "Total Deposits" }),
428
+ /* @__PURE__ */ jsx(
429
+ "span",
430
+ {
431
+ className: `${result_item_value} ${depositsScrambling ? scrambling : ""}`,
432
+ children: totalDepositsDisplay
433
+ }
434
+ )
435
+ ] })
436
+ ] })
437
+ ] }) })
438
+ ]
439
+ }
440
+ )
441
+ ]
442
+ }
443
+ );
325
444
  };
326
445
  export {
327
446
  ApyCalculator