@homefile/components-v2 2.59.1 → 2.60.0
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.
- package/dist/assets/locales/en/index.json +5 -2
- package/dist/components/folderPanel/displayFiles/GridFile.js +9 -1
- package/dist/components/folderPanel/displayFiles/ListFile.js +9 -1
- package/dist/components/forms/dynamicForm/DynamicForm.js +1 -1
- package/dist/components/forms/dynamicForm/HiddenField.js +2 -2
- package/dist/components/forms/dynamicForm/fields/HomeInitialSetup.js +3 -1
- package/dist/components/forms/readOnly/ReadOnlyDynamicForm.js +10 -2
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyAppliances.js +21 -22
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyGuidelines.js +6 -3
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyHistory.d.ts +2 -0
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyHistory.js +35 -0
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyItemRelated.js +8 -3
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyOverview.d.ts +2 -0
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyOverview.js +16 -0
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyPurchaseDetails.d.ts +2 -0
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyPurchaseDetails.js +30 -0
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyWarrantyBanner.d.ts +2 -0
- package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyWarrantyBanner.js +42 -0
- package/dist/components/forms/readOnly/fields/homeItem/index.d.ts +4 -0
- package/dist/components/forms/readOnly/fields/homeItem/index.js +4 -0
- package/dist/components/headers/TabsHeader.js +1 -1
- package/dist/components/history/AddDocuments.d.ts +7 -0
- package/dist/components/history/AddDocuments.js +8 -0
- package/dist/components/history/DatePennant.d.ts +7 -0
- package/dist/components/history/DatePennant.js +6 -0
- package/dist/components/history/HistoryCard.d.ts +19 -0
- package/dist/components/history/HistoryCard.js +13 -0
- package/dist/components/history/index.d.ts +3 -0
- package/dist/components/history/index.js +3 -0
- package/dist/components/homeBoard/AddToHomefile.js +1 -1
- package/dist/components/homeBoard/HomeCard.js +1 -1
- package/dist/components/homeItems/ViewingHomeItemBody.d.ts +2 -0
- package/dist/components/homeItems/ViewingHomeItemBody.js +41 -0
- package/dist/components/homeItems/ViewingHomeItemPanel.js +17 -19
- package/dist/components/homeItems/index.d.ts +1 -0
- package/dist/components/homeItems/index.js +1 -0
- package/dist/components/homi/HomiPresence.d.ts +2 -0
- package/dist/components/homi/HomiPresence.js +31 -0
- package/dist/components/homi/HomiVoice.d.ts +2 -0
- package/dist/components/homi/HomiVoice.js +6 -0
- package/dist/components/homi/ProgressBar.d.ts +2 -0
- package/dist/components/homi/ProgressBar.js +9 -0
- package/dist/components/homi/RevealValueCard.d.ts +2 -0
- package/dist/components/homi/RevealValueCard.js +23 -0
- package/dist/components/homi/StaggerReveal.d.ts +2 -0
- package/dist/components/homi/StaggerReveal.js +29 -0
- package/dist/components/homi/index.d.ts +5 -0
- package/dist/components/homi/index.js +5 -0
- package/dist/components/inboxTile/InboxForwardBanner.js +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +2 -0
- package/dist/components/inputs/InlineEditField.d.ts +2 -0
- package/dist/components/inputs/InlineEditField.js +15 -0
- package/dist/components/inputs/RadioCard.d.ts +3 -1
- package/dist/components/inputs/RadioCard.js +3 -3
- package/dist/components/inputs/TextInput.d.ts +1 -1
- package/dist/components/inputs/TextInput.js +11 -4
- package/dist/components/inputs/index.d.ts +1 -0
- package/dist/components/inputs/index.js +1 -0
- package/dist/components/myHomes/HomeDetailsContent.js +1 -1
- package/dist/components/myHomes/IncludePublicRecords.d.ts +13 -1
- package/dist/components/myHomes/IncludePublicRecords.js +16 -6
- package/dist/components/myHomes/edithome/EditHomeDetails.js +12 -3
- package/dist/components/myHomes/steps/NewHomeDetails.d.ts +1 -1
- package/dist/components/myHomes/steps/NewHomeDetails.js +3 -3
- package/dist/components/myProfile/MyProfileBody.d.ts +1 -1
- package/dist/components/myProfile/MyProfileBody.js +2 -2
- package/dist/components/myProfile/details/HomiSms.d.ts +1 -1
- package/dist/components/myProfile/details/HomiSms.js +53 -23
- package/dist/components/myProfile/details/TwoFactorSetting.js +2 -1
- package/dist/components/propertyTiles/propertyTaxes/PropertyTaxes.js +1 -1
- package/dist/components/propertyTiles/trendingValue/TrendingValue.js +1 -1
- package/dist/components/rooms/RoomHeader.d.ts +1 -1
- package/dist/components/rooms/RoomHeader.js +3 -3
- package/dist/components/wizard/ControlledWizard.d.ts +1 -1
- package/dist/components/wizard/ControlledWizard.js +42 -3
- package/dist/components/wizard/WizardInitialOptions.js +3 -3
- package/dist/components/wizard/WizardSelectCard.d.ts +14 -0
- package/dist/components/wizard/WizardSelectCard.js +32 -0
- package/dist/components/wizard/WizardStepPrompt.d.ts +11 -0
- package/dist/components/wizard/WizardStepPrompt.js +20 -0
- package/dist/components/wizard/WizardSuccessHeader.d.ts +1 -1
- package/dist/components/wizard/WizardSuccessHeader.js +2 -2
- package/dist/components/wizard/index.d.ts +2 -0
- package/dist/components/wizard/index.js +2 -0
- package/dist/helpers/forms/dynamicForm.helper.js +4 -0
- package/dist/hooks/folderPanel/useDisplayFiles.js +11 -2
- package/dist/hooks/forms/useAddressStored.js +8 -2
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/motion/index.d.ts +2 -0
- package/dist/hooks/motion/index.js +2 -0
- package/dist/hooks/motion/useCountUp.d.ts +10 -0
- package/dist/hooks/motion/useCountUp.js +39 -0
- package/dist/hooks/motion/usePrefersReducedMotion.d.ts +14 -0
- package/dist/hooks/motion/usePrefersReducedMotion.js +15 -0
- package/dist/hooks/rooms/useRoomHeader.d.ts +4 -1
- package/dist/hooks/rooms/useRoomHeader.js +9 -1
- package/dist/hooks/useDirtySave.d.ts +14 -0
- package/dist/hooks/useDirtySave.js +33 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/interfaces/forms/dynamicForm/DynamicForm.interface.d.ts +7 -3
- package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyOverview.interface.d.ts +2 -0
- package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyOverview.interface.js +1 -0
- package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyPurchaseDetails.interface.d.ts +5 -0
- package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyPurchaseDetails.interface.js +1 -0
- package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyWarrantyBanner.interface.d.ts +27 -0
- package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyWarrantyBanner.interface.js +1 -0
- package/dist/interfaces/forms/readOnly/homeItem/index.d.ts +3 -0
- package/dist/interfaces/forms/readOnly/homeItem/index.js +3 -0
- package/dist/interfaces/homeItems/ViewingHomeItemPanel.interface.d.ts +1 -0
- package/dist/interfaces/homi/HomiPresence.interface.d.ts +9 -0
- package/dist/interfaces/homi/HomiPresence.interface.js +1 -0
- package/dist/interfaces/homi/ProgressBar.interface.d.ts +8 -0
- package/dist/interfaces/homi/ProgressBar.interface.js +1 -0
- package/dist/interfaces/homi/RevealValueCard.interface.d.ts +16 -0
- package/dist/interfaces/homi/RevealValueCard.interface.js +1 -0
- package/dist/interfaces/homi/StaggerReveal.interface.d.ts +9 -0
- package/dist/interfaces/homi/StaggerReveal.interface.js +1 -0
- package/dist/interfaces/homi/index.d.ts +4 -0
- package/dist/interfaces/homi/index.js +4 -0
- package/dist/interfaces/index.d.ts +1 -0
- package/dist/interfaces/index.js +1 -0
- package/dist/interfaces/inputs/InlineEditField.interface.d.ts +7 -0
- package/dist/interfaces/inputs/InlineEditField.interface.js +1 -0
- package/dist/interfaces/inputs/Input.interface.d.ts +6 -1
- package/dist/interfaces/inputs/index.d.ts +1 -0
- package/dist/interfaces/inputs/index.js +1 -0
- package/dist/interfaces/myHomes/AddHomeContent.interface.d.ts +3 -0
- package/dist/interfaces/myProfile/MyProfileBody.interface.d.ts +1 -0
- package/dist/interfaces/myProfile/details/HomiSms.interface.d.ts +11 -2
- package/dist/interfaces/rooms/RoomHeader.interface.d.ts +5 -0
- package/dist/interfaces/wizard/ControlledWizard.interface.d.ts +6 -0
- package/dist/interfaces/wizard/WizardStepPrompt.interface.d.ts +9 -0
- package/dist/interfaces/wizard/WizardStepPrompt.interface.js +1 -0
- package/dist/interfaces/wizard/index.d.ts +1 -0
- package/dist/interfaces/wizard/index.js +1 -0
- package/dist/stories/forms/readOnly/ReadOnlyAppliances.stories.d.ts +4 -0
- package/dist/stories/forms/readOnly/ReadOnlyAppliances.stories.js +33 -0
- package/dist/stories/forms/readOnly/ReadOnlyWarrantyBanner.stories.d.ts +4 -0
- package/dist/stories/forms/readOnly/ReadOnlyWarrantyBanner.stories.js +10 -0
- package/dist/stories/homeItems/ReadOnlyHistory.stories.d.ts +4 -0
- package/dist/stories/homeItems/ReadOnlyHistory.stories.js +46 -0
- package/dist/stories/homeItems/ReadOnlyOverview.stories.d.ts +5 -0
- package/dist/stories/homeItems/ReadOnlyOverview.stories.js +69 -0
- package/dist/stories/homeItems/ViewingHomeItemPanel.stories.d.ts +4 -0
- package/dist/stories/homeItems/ViewingHomeItemPanel.stories.js +204 -0
- package/dist/stories/homi/HomiPresence.stories.d.ts +18 -0
- package/dist/stories/homi/HomiPresence.stories.js +11 -0
- package/dist/stories/homi/HomiVoice.stories.d.ts +4 -0
- package/dist/stories/homi/HomiVoice.stories.js +8 -0
- package/dist/stories/homi/ProgressBar.stories.d.ts +10 -0
- package/dist/stories/homi/ProgressBar.stories.js +9 -0
- package/dist/stories/homi/RevealValueCard.stories.d.ts +19 -0
- package/dist/stories/homi/RevealValueCard.stories.js +22 -0
- package/dist/stories/homi/StaggerReveal.stories.d.ts +5 -0
- package/dist/stories/homi/StaggerReveal.stories.js +8 -0
- package/dist/stories/inputs/InlineEditField.stories.d.ts +10 -0
- package/dist/stories/inputs/InlineEditField.stories.js +11 -0
- package/dist/stories/myProfile/MyProfilePanel.stories.js +1 -1
- package/dist/stories/myProfile/details/HomiSms.stories.d.ts +3 -2
- package/dist/stories/myProfile/details/HomiSms.stories.js +3 -2
- package/dist/stories/wizard/ControlledWizard.stories.d.ts +9 -0
- package/dist/stories/wizard/ControlledWizard.stories.js +41 -60
- package/dist/theme/index.d.ts +3 -0
- package/dist/theme/index.js +7 -4
- package/dist/utils/Formaters.utils.d.ts +2 -0
- package/dist/utils/Formaters.utils.js +14 -0
- package/dist/utils/Weather.utils.js +11 -4
- package/package.json +1 -1
|
@@ -3,33 +3,63 @@ import { t } from 'i18next';
|
|
|
3
3
|
import { Controller, useForm, useWatch } from 'react-hook-form';
|
|
4
4
|
import { useEffect, useState } from 'react';
|
|
5
5
|
import * as yup from 'yup';
|
|
6
|
-
import { Stack, Flex, Img, Text, Box, FormControl, FormLabel, Switch,
|
|
6
|
+
import { Stack, Flex, Img, Text, Box, FormControl, FormLabel, Switch, Link, } from '@chakra-ui/react';
|
|
7
7
|
import { SMS } from '../../../assets/images';
|
|
8
8
|
import { ControlledInput, Loading } from '../../../components';
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
import { formatPhone, toPhoneDigits } from '../../../utils';
|
|
10
|
+
// "Enable Homi SMS Messaging" account setting. The ON/OFF toggle IS the enable
|
|
11
|
+
// — no separate SMS checkbox (redundant). The toggle is controllable
|
|
12
|
+
// (`enabled`) so it reflects real state, e.g. ON with the user's number for
|
|
13
|
+
// accounts Homi created via SMS. The phone is disabled until enabled and is
|
|
14
|
+
// pre-filled by the app from the 2FA-SMS number when present. A consent link
|
|
15
|
+
// (`consentUrl`) points to the SMS messaging consent language (legal's
|
|
16
|
+
// content). Saving is IMPLICIT — flipping the toggle saves immediately, and the
|
|
17
|
+
// number saves on blur once valid — so there is no Save button.
|
|
18
|
+
// `mode='phone-only'` renders just the phone field for flows that only need the
|
|
19
|
+
// number.
|
|
20
|
+
export const HomiSms = ({ mode = 'full', enabled = false, phone = '', consentUrl, loading, onChange, onSave, }) => {
|
|
11
21
|
const isPhoneOnly = mode === 'phone-only';
|
|
12
|
-
const { control,
|
|
13
|
-
defaultValues: { sms },
|
|
14
|
-
});
|
|
22
|
+
const { control, trigger } = useForm({ defaultValues: { phone } });
|
|
15
23
|
const values = useWatch({ control });
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
?
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return yup.string().required().min(10).max(10).isValidSync(value);
|
|
25
|
-
if (isEnabled && isSmsEnabled)
|
|
26
|
-
return yup.string().required().min(10).max(10).isValidSync(value);
|
|
27
|
-
return true;
|
|
28
|
-
};
|
|
24
|
+
const [isEnabled, setIsEnabled] = useState(enabled);
|
|
25
|
+
const isPhoneValid = (value) => yup.string().required().min(10).max(10).isValidSync(value);
|
|
26
|
+
// Optional live-change listener.
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
onChange === null || onChange === void 0 ? void 0 : onChange({ enabled: isEnabled, phone: values.phone });
|
|
29
|
+
}, [onChange, values, isEnabled]);
|
|
30
|
+
// Enabling requires a phone number: turning it on surfaces the required
|
|
31
|
+
// error until a valid number is entered.
|
|
29
32
|
useEffect(() => {
|
|
30
|
-
if (
|
|
31
|
-
|
|
33
|
+
if (isEnabled)
|
|
34
|
+
trigger('phone');
|
|
35
|
+
}, [isEnabled, trigger]);
|
|
36
|
+
// Implicit save: the toggle saves the instant it flips; the number saves on
|
|
37
|
+
// blur once it is valid. No Save button. Turning ON only persists `enabled`
|
|
38
|
+
// once there is a valid number (otherwise the error shows and the save waits
|
|
39
|
+
// for the number's blur); turning OFF always saves immediately.
|
|
40
|
+
const handleToggle = (checked) => {
|
|
41
|
+
setIsEnabled(checked);
|
|
42
|
+
if (!checked)
|
|
43
|
+
onSave === null || onSave === void 0 ? void 0 : onSave({ enabled: false, phone: values.phone });
|
|
44
|
+
else if (isPhoneValid(values.phone))
|
|
45
|
+
onSave === null || onSave === void 0 ? void 0 : onSave({ enabled: true, phone: values.phone });
|
|
46
|
+
};
|
|
47
|
+
const handlePhoneBlur = (value, fieldBlur) => {
|
|
48
|
+
fieldBlur === null || fieldBlur === void 0 ? void 0 : fieldBlur();
|
|
49
|
+
// Cleared number → DELETE it, and turn messaging off (it can't be on
|
|
50
|
+
// without a number). This is distinct from toggling off, which pauses
|
|
51
|
+
// messaging but keeps the number. The 2FA number is unaffected either way.
|
|
52
|
+
if (toPhoneDigits(value).length === 0) {
|
|
53
|
+
setIsEnabled(false);
|
|
54
|
+
onSave === null || onSave === void 0 ? void 0 : onSave({ enabled: false, phone: '' });
|
|
55
|
+
return;
|
|
32
56
|
}
|
|
33
|
-
|
|
34
|
-
|
|
57
|
+
if (!isPhoneValid(value))
|
|
58
|
+
return;
|
|
59
|
+
if (isPhoneOnly)
|
|
60
|
+
onSave === null || onSave === void 0 ? void 0 : onSave({ phone: value });
|
|
61
|
+
else if (isEnabled)
|
|
62
|
+
onSave === null || onSave === void 0 ? void 0 : onSave({ enabled: isEnabled, phone: value });
|
|
63
|
+
};
|
|
64
|
+
return (_jsxs(Box, { position: "relative", children: [loading && _jsx(Loading, {}), _jsxs(Stack, { bg: "lightBlue.2", px: "base", py: "4", spacing: "4", children: [_jsxs(Stack, { spacing: "3", children: [_jsxs(Flex, { align: "center", justify: "space-between", children: [_jsxs(Flex, { gap: "base", align: "center", children: [_jsx(Img, { src: SMS, alt: t('myProfile.details.homiSmsAlt') }), _jsx(Text, { fontSize: "sm", textTransform: "uppercase", children: t('myProfile.details.homiSmsTitle') })] }), !isPhoneOnly && (_jsxs(FormControl, { display: "flex", alignItems: "center", w: "fit-content", children: [_jsx(FormLabel, { htmlFor: "homi-sms", mb: "0", fontSize: "sm", children: isEnabled ? 'ON' : 'OFF' }), _jsx(Switch, { id: "homi-sms", size: "lg", isChecked: isEnabled, onChange: (e) => handleToggle(e.target.checked) })] }))] }), !isPhoneOnly && (_jsxs(Stack, { spacing: "1", children: [_jsx(Text, { fontFamily: "secondary", lineHeight: "1.2", children: t('myProfile.details.homiSmsSubtitle') }), consentUrl && (_jsx(Link, { href: consentUrl, isExternal: true, w: "fit-content", fontFamily: "secondary", fontSize: "sm", color: "blue.2", textDecoration: "underline", children: t('myProfile.details.homiSmsConsent') }))] }))] }), _jsx(Controller, { name: "phone", control: control, rules: { validate: (value) => isPhoneOnly || !isEnabled || isPhoneValid(value) }, render: ({ field: { value, onBlur, onChange }, fieldState: { invalid }, }) => (_jsx(ControlledInput, { placeholder: t('myProfile.placeholders.sms'), onChange: (e) => onChange(toPhoneDigits(e.target.value)), onBlur: () => handlePhoneBlur(value, onBlur), value: formatPhone(value), type: "tel", hasError: invalid, errorMessage: `${t('forms.invalid')} ${t('forms.phone')}`, isDisabled: !isPhoneOnly && !isEnabled })) })] })] }));
|
|
35
65
|
};
|
|
@@ -4,6 +4,7 @@ import { Controller } from 'react-hook-form';
|
|
|
4
4
|
import { Stack, Flex, Img, Text, Box, FormControl, FormLabel, Switch, Button, } from '@chakra-ui/react';
|
|
5
5
|
import { Security } from '../../../assets/images';
|
|
6
6
|
import { ControlledInput, Loading, RadioButton, TextInput, } from '../../../components';
|
|
7
|
+
import { formatPhone, toPhoneDigits } from '../../../utils';
|
|
7
8
|
import { useTwoFactorSetting } from '../../../hooks';
|
|
8
9
|
export const TwoFactorSetting = ({ defaultValue = 'email', email = '', loading, onChange, onSave, sms = '', twoFactor, }) => {
|
|
9
10
|
const { altText, control, emailRadio, group, handleEmailValidation, handlePhoneValidation, handleSubmit, is2faEnabled, radioValue, showButtons, smsRadio, } = useTwoFactorSetting({
|
|
@@ -17,5 +18,5 @@ export const TwoFactorSetting = ({ defaultValue = 'email', email = '', loading,
|
|
|
17
18
|
return (_jsxs(Box, { position: "relative", children: [loading && _jsx(Loading, {}), _jsxs(Stack, { bg: "lightBlue.1", px: "base", py: "4", spacing: "8", children: [_jsxs(Stack, { spacing: "4", children: [_jsxs(Flex, { align: "center", justify: "space-between", children: [_jsxs(Flex, { gap: "base", align: "center", children: [_jsx(Img, { src: Security, alt: altText }), _jsx(Text, { fontSize: "sm", textTransform: "uppercase", children: t('myProfile.details.2FactorTitle') })] }), _jsx(Controller, { name: "2fa", control: control, render: ({ field: { value, onChange, onBlur } }) => {
|
|
18
19
|
const label = value ? 'ON' : 'OFF';
|
|
19
20
|
return (_jsxs(FormControl, { display: "flex", alignItems: "center", w: "fit-content", children: [_jsx(FormLabel, { htmlFor: "2fa", mb: "0", fontSize: "sm", children: label }), _jsx(Switch, { id: "2fa", size: "lg", isChecked: value, onChange: onChange, onBlur: onBlur })] }));
|
|
20
|
-
} })] }), _jsx(Text, { fontFamily: "secondary", lineHeight: "1.2", children: t('myProfile.details.2FactorSubtitle') })] }), _jsxs(Stack, Object.assign({}, group, { spacing: "4", children: [_jsxs(Flex, { gap: "base", align: "center", justify: "space-between", children: [_jsx(RadioButton, Object.assign({}, emailRadio, { isDisabled: !is2faEnabled, children: "Email" })), _jsx(Box, { w: "75%", children: _jsx(Controller, { name: "email", control: control, rules: { validate: handleEmailValidation }, render: ({ field: { value, onBlur, onChange }, fieldState: { invalid }, }) => (_jsx(TextInput, { placeholder: t('myProfile.placeholders.email'), handleChange: onChange, onBlur: onBlur, value: value, hasError: invalid, errorMessage: `${t('forms.invalid')} ${t('forms.email')}`, isDisabled: true })) }) })] }), _jsxs(Flex, { gap: "base", align: "center", justify: "space-between", children: [_jsx(RadioButton, Object.assign({}, smsRadio, { isDisabled: !is2faEnabled, children: "SMS" })), _jsx(Box, { w: "75%", children: _jsx(Controller, { name: "sms", control: control, rules: { validate: handlePhoneValidation }, render: ({ field: { value, onBlur, onChange }, fieldState: { invalid }, }) => (_jsx(ControlledInput, { placeholder: t('myProfile.placeholders.sms'), onChange: onChange, onBlur: onBlur, value: value, type: "
|
|
21
|
+
} })] }), _jsx(Text, { fontFamily: "secondary", lineHeight: "1.2", children: t('myProfile.details.2FactorSubtitle') })] }), _jsxs(Stack, Object.assign({}, group, { spacing: "4", children: [_jsxs(Flex, { gap: "base", align: "center", justify: "space-between", children: [_jsx(RadioButton, Object.assign({}, emailRadio, { isDisabled: !is2faEnabled, children: "Email" })), _jsx(Box, { w: "75%", children: _jsx(Controller, { name: "email", control: control, rules: { validate: handleEmailValidation }, render: ({ field: { value, onBlur, onChange }, fieldState: { invalid }, }) => (_jsx(TextInput, { placeholder: t('myProfile.placeholders.email'), handleChange: onChange, onBlur: onBlur, value: value, hasError: invalid, errorMessage: `${t('forms.invalid')} ${t('forms.email')}`, isDisabled: true })) }) })] }), _jsxs(Flex, { gap: "base", align: "center", justify: "space-between", children: [_jsx(RadioButton, Object.assign({}, smsRadio, { isDisabled: !is2faEnabled, children: "SMS" })), _jsx(Box, { w: "75%", children: _jsx(Controller, { name: "sms", control: control, rules: { validate: handlePhoneValidation }, render: ({ field: { value, onBlur, onChange }, fieldState: { invalid }, }) => (_jsx(ControlledInput, { placeholder: t('myProfile.placeholders.sms'), onChange: (e) => onChange(toPhoneDigits(e.target.value)), onBlur: onBlur, value: formatPhone(value), type: "tel", hasError: invalid, errorMessage: `${t('forms.invalid')} ${t('forms.phone')}`, isDisabled: radioValue === 'email' || !is2faEnabled })) }) })] }), _jsx(Box, { w: "75%", alignSelf: "end", children: showButtons && (_jsx(Button, { variant: "secondaryFooter", type: "submit", bg: "neutral.white", fontWeight: "medium", onClick: handleSubmit(onSave), children: t('buttons.save').toUpperCase() })) })] }))] })] }));
|
|
21
22
|
};
|
|
@@ -15,5 +15,5 @@ export const PropertyTaxes = ({ estimatedTaxValue, estimatedMonthlyAverage, menu
|
|
|
15
15
|
colorPositive: 'orange.1',
|
|
16
16
|
});
|
|
17
17
|
const currentYear = new Date().getFullYear();
|
|
18
|
-
return (_jsx(TileTooltip, { label: "propertyTaxes", children: _jsx(Box, { children: _jsxs(TileCard, { menuItems: menuItems, title: t('propertyTiles.taxes.title'), icon: Money, fontWeight: "regular", showHelp: showHelp, children: [title && (_jsx(Box, { bg: "lightBlue.2", px: "base", py: "4", children: _jsx(Text, { variant: "home", children: title }) })), _jsxs(Box, { px: "base", bg: "neutral.white", children: [_jsx(AssessedValue, Object.assign({}, lastYear, { value: formattedLastYearValue })), _jsxs(Stack, { spacing: "1", py: "base", children: [_jsx(Text, { textTransform: "uppercase", children: `${currentYear} ${t('propertyTiles.taxes.estimated')}` }), _jsxs(Flex, { gap: "2", w: "full", children: [_jsx(Box, { flex: "3", children: _jsx(TotalHeader, { bg: bgColor, value: estimatedTaxValue, fontSize: "3xl", title: t('propertyTiles.taxes.annually') }) }), estimatedMonthlyAverage && (_jsx(Box, { flex: "2", children: _jsx(TotalHeader, { bg: "lightBlue.4", value: estimatedMonthlyAverage, fontSize: "3xl", title: t('propertyTiles.taxes.monthly') }) }))] })] })] }), _jsx(Box, { px: "base", children: _jsx(TaxesTable, { taxes: taxes }) })] }) }) }));
|
|
18
|
+
return (_jsx(TileTooltip, { label: "propertyTaxes", children: _jsx(Box, { h: "full", children: _jsxs(TileCard, { menuItems: menuItems, title: t('propertyTiles.taxes.title'), icon: Money, fontWeight: "regular", showHelp: showHelp, children: [title && (_jsx(Box, { bg: "lightBlue.2", px: "base", py: "4", children: _jsx(Text, { variant: "home", children: title }) })), _jsxs(Box, { px: "base", bg: "neutral.white", children: [_jsx(AssessedValue, Object.assign({}, lastYear, { value: formattedLastYearValue })), _jsxs(Stack, { spacing: "1", py: "base", children: [_jsx(Text, { textTransform: "uppercase", children: `${currentYear} ${t('propertyTiles.taxes.estimated')}` }), _jsxs(Flex, { gap: "2", w: "full", children: [_jsx(Box, { flex: "3", children: _jsx(TotalHeader, { bg: bgColor, value: estimatedTaxValue, fontSize: "3xl", title: t('propertyTiles.taxes.annually') }) }), estimatedMonthlyAverage && (_jsx(Box, { flex: "2", children: _jsx(TotalHeader, { bg: "lightBlue.4", value: estimatedMonthlyAverage, fontSize: "3xl", title: t('propertyTiles.taxes.monthly') }) }))] })] })] }), _jsx(Box, { px: "base", children: _jsx(TaxesTable, { taxes: taxes }) })] }) }) }));
|
|
19
19
|
};
|
|
@@ -9,5 +9,5 @@ export const TrendingValue = ({ chartData, marketValue = 0, menuItems, mortgageB
|
|
|
9
9
|
// Purchase price + mortgage are "set once" inputs — collapsed by default so
|
|
10
10
|
// the value + chart get the room (future: a "predicted" segment on the chart).
|
|
11
11
|
const [showInputs, setShowInputs] = useState(false);
|
|
12
|
-
return (_jsx(TileTooltip, { label: "trendingValue", children: _jsx(Box, { children: _jsxs(TileCard, { bg: "lightGreen.8", menuItems: menuItems, title: t('propertyTiles.trending.title'), icon: ComputerReport, fontWeight: "normal", showHelp: showHelp, children: [_jsxs(Flex, { as: "button", onClick: () => setShowInputs((v) => !v), align: "center", justify: "space-between", w: "full", px: "base", py: "2", bg: "lightBlue.1", children: [_jsx(Text, { fontSize: "xs", textTransform: "uppercase", color: "gray.3", children: "Purchase & Mortgage" }), showInputs ? _jsx(ChevronUp, { size: 16 }) : _jsx(ChevronDown, { size: 16 })] }), _jsx(Collapse, { in: showInputs, animateOpacity: true, children: _jsxs(Flex, { gap: "base", px: "base", pb: "base", bg: "lightBlue.1", children: [_jsx(TrendingValueCard, { bg: "lightBlue.2", label: t('propertyTiles.trending.purchase'), value: purchasePrice }), (mortgageBalance > 0) ? (_jsx(TrendingValueCard, { bg: "lightBlue.4", editButton: Boolean(mortgageBalance) && (_jsx(Tooltip, { label: t('tooltips.editMortgage'), children: _jsx(IconButton, { variant: "menuIconWithShadow", maxW: "fit-content", "aria-label": t('ariaLabels.edit'), p: "1", icon: _jsx(Edit, { size: 16 }), onClick: onMortgageClick }) })), label: t('propertyTiles.trending.mortgage'), value: mortgageBalance })) : (_jsxs(Stack, { spacing: "1", flex: "auto", children: [_jsx(Flex, { align: "center", justify: "space-between", h: "20px", children: _jsx(Text, { fontSize: "xs", textTransform: "uppercase", children: t('propertyTiles.trending.mortgage') }) }), _jsxs(Button, { variant: "tertiary", h: "full", onClick: onMortgageClick, w: "full", px: "1", children: [_jsx(Plus, { stroke: colors.blue[3], size: 20 }), _jsx(Text, { color: colors.blue[3], as: "span", pl: '.25rem', children: t('propertyTiles.trending.addMortgage') })] })] }))] }) }), _jsxs(Stack, { spacing: "base", p: "base", children: [_jsxs(Flex, { children: [_jsx(TrendingValueCard, { bg: "lightGreen.6", fontSize: "3xl", label: t('propertyTiles.trending.estimated'), value: marketValue }), marketValue > 0 && (_jsx(EstEquity, { value: marketValue - mortgageBalance }))] }), chartData && (_jsx(TrendingLineChart, { data: chartData, purchasePrice: purchasePrice })), !chartData && (_jsx(Box, { h: "140px", border: "1px solid", borderColor: "lightGreen.6" })), _jsx(RefinanceCta, {})] })] }) }) }));
|
|
12
|
+
return (_jsx(TileTooltip, { label: "trendingValue", children: _jsx(Box, { h: "full", children: _jsxs(TileCard, { bg: "lightGreen.8", menuItems: menuItems, title: t('propertyTiles.trending.title'), icon: ComputerReport, fontWeight: "normal", showHelp: showHelp, children: [_jsxs(Flex, { as: "button", onClick: () => setShowInputs((v) => !v), align: "center", justify: "space-between", w: "full", px: "base", py: "2", bg: "lightBlue.1", children: [_jsx(Text, { fontSize: "xs", textTransform: "uppercase", color: "gray.3", children: "Purchase & Mortgage" }), showInputs ? _jsx(ChevronUp, { size: 16 }) : _jsx(ChevronDown, { size: 16 })] }), _jsx(Collapse, { in: showInputs, animateOpacity: true, children: _jsxs(Flex, { gap: "base", px: "base", pb: "base", bg: "lightBlue.1", children: [_jsx(TrendingValueCard, { bg: "lightBlue.2", label: t('propertyTiles.trending.purchase'), value: purchasePrice }), (mortgageBalance > 0) ? (_jsx(TrendingValueCard, { bg: "lightBlue.4", editButton: Boolean(mortgageBalance) && (_jsx(Tooltip, { label: t('tooltips.editMortgage'), children: _jsx(IconButton, { variant: "menuIconWithShadow", maxW: "fit-content", "aria-label": t('ariaLabels.edit'), p: "1", icon: _jsx(Edit, { size: 16 }), onClick: onMortgageClick }) })), label: t('propertyTiles.trending.mortgage'), value: mortgageBalance })) : (_jsxs(Stack, { spacing: "1", flex: "auto", children: [_jsx(Flex, { align: "center", justify: "space-between", h: "20px", children: _jsx(Text, { fontSize: "xs", textTransform: "uppercase", children: t('propertyTiles.trending.mortgage') }) }), _jsxs(Button, { variant: "tertiary", h: "full", onClick: onMortgageClick, w: "full", px: "1", children: [_jsx(Plus, { stroke: colors.blue[3], size: 20 }), _jsx(Text, { color: colors.blue[3], as: "span", pl: '.25rem', children: t('propertyTiles.trending.addMortgage') })] })] }))] }) }), _jsxs(Stack, { spacing: "base", p: "base", children: [_jsxs(Flex, { children: [_jsx(TrendingValueCard, { bg: "lightGreen.6", fontSize: "3xl", label: t('propertyTiles.trending.estimated'), value: marketValue }), marketValue > 0 && (_jsx(EstEquity, { value: marketValue - mortgageBalance }))] }), chartData && (_jsx(TrendingLineChart, { data: chartData, purchasePrice: purchasePrice })), !chartData && (_jsx(Box, { h: "140px", border: "1px solid", borderColor: "lightGreen.6" })), _jsx(RefinanceCta, {})] })] }) }) }));
|
|
13
13
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { RoomHeaderI } from '../../interfaces';
|
|
2
|
-
export declare const RoomHeader: ({ isDeleteDisabled, onClickDelete, onSubmit, room, roomAmount, disabled, }: RoomHeaderI) => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const RoomHeader: ({ isDeleteDisabled, onClickDelete, onSubmit, room, roomAmount, roomEstimatedAmount, disabled, }: RoomHeaderI) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,9 +4,9 @@ import { Box, Flex, Center, Image, IconButton, Text, Show, Button, } from '@chak
|
|
|
4
4
|
import { TextInput, TrashIcon, Edit } from '../../components';
|
|
5
5
|
import { roomIcons } from '../../helpers';
|
|
6
6
|
import { useRoomHeader } from '../../hooks';
|
|
7
|
-
export const RoomHeader = ({ isDeleteDisabled, onClickDelete, onSubmit, room, roomAmount, disabled, }) => {
|
|
8
|
-
const { formattedAmount, handleChange, handleClick, inputValue, isEditing } = useRoomHeader({ onSubmit, room, roomAmount });
|
|
7
|
+
export const RoomHeader = ({ isDeleteDisabled, onClickDelete, onSubmit, room, roomAmount, roomEstimatedAmount, disabled, }) => {
|
|
8
|
+
const { formattedAmount, formattedEstimatedAmount, hasEstimate, handleChange, handleClick, inputValue, isEditing, } = useRoomHeader({ onSubmit, room, roomAmount, roomEstimatedAmount });
|
|
9
9
|
return (_jsx(Box, { w: "full", bg: "lightBlue.4", py: "2", px: "base", "data-testid": "room-header", children: _jsxs(Flex, { justify: "space-between", align: "center", children: [_jsxs(Flex, { w: "fit-content", children: [_jsx(Center, { h: "2.3rem", minW: "2.6rem", mr: "2", children: _jsx(Image, { alt: room.name, maxH: "2rem", src: roomIcons[room.type], w: "auto" }) }), _jsx(TextInput, { fontWeight: isEditing ? 'regular' : 'medium', fontSize: "1.25rem", fontFamily: "primary", handleChange: handleChange, height: "full", id: room.name, isDisabled: !isEditing || disabled, placeholder: "", value: isEditing && !disabled
|
|
10
10
|
? inputValue || ''
|
|
11
|
-
: (inputValue === null || inputValue === void 0 ? void 0 : inputValue.toUpperCase()) || '' }), isEditing && !disabled ? (_jsx(Button, { variant: "tertiary", onClick: handleClick, ml: "2", children: t('roomHeader.saveBtn') })) : (_jsx(IconButton, { "aria-label": t('ariaLabels.edit'), icon: _jsx(Edit, {}), maxW: "fit-content", onClick: handleClick, px: "2", variant: "menuIconWithShadow", id: "customizeRoomName", disabled: disabled }))] }), _jsxs(Flex, { align: "center", gap: "6", children: [_jsxs(Flex, { align: "center", gap: "6", id: "trackRoomValue", bg: "lightBlue.4", children: [_jsx(Show, { breakpoint: "(min-width: 600px)", children: _jsx(Text, { fontWeight: "medium", variant: "caption", children: t('roomHeader.roomValue').toUpperCase() }) }), _jsx(Text, { variant: "titleBolder", children: formattedAmount }), _jsx(Show, { breakpoint: "(min-width: 600px)", children: _jsx(Box, { h: "5", w: "1px", bg: "lightBlue.6" }) })] }), _jsx(IconButton, { "aria-label": t('ariaLabels.delete'), icon: _jsx(TrashIcon, {}), maxW: "fit-content", onClick: () => onClickDelete(room._id), disabled: isDeleteDisabled, px: "1", variant: "menuIconWithShadow" })] })] }) }));
|
|
11
|
+
: (inputValue === null || inputValue === void 0 ? void 0 : inputValue.toUpperCase()) || '' }), isEditing && !disabled ? (_jsx(Button, { variant: "tertiary", onClick: handleClick, ml: "2", children: t('roomHeader.saveBtn') })) : (_jsx(IconButton, { "aria-label": t('ariaLabels.edit'), icon: _jsx(Edit, {}), maxW: "fit-content", onClick: handleClick, px: "2", variant: "menuIconWithShadow", id: "customizeRoomName", disabled: disabled }))] }), _jsxs(Flex, { align: "center", gap: "6", children: [_jsxs(Flex, { align: "center", gap: "6", id: "trackRoomValue", bg: "lightBlue.4", children: [_jsx(Show, { breakpoint: "(min-width: 600px)", children: _jsx(Text, { fontWeight: "medium", variant: "caption", children: t('roomHeader.roomValue').toUpperCase() }) }), _jsx(Text, { variant: "titleBolder", children: formattedAmount }), _jsx(Show, { breakpoint: "(min-width: 600px)", children: _jsx(Box, { h: "5", w: "1px", bg: "lightBlue.6" }) })] }), hasEstimate && (_jsxs(Flex, { align: "center", gap: "6", id: "trackRoomEstimatedValue", bg: "lightBlue.4", children: [_jsx(Show, { breakpoint: "(min-width: 600px)", children: _jsx(Text, { fontWeight: "medium", variant: "caption", children: t('roomHeader.estValue').toUpperCase() }) }), _jsx(Text, { variant: "titleBolder", children: formattedEstimatedAmount }), _jsx(Show, { breakpoint: "(min-width: 600px)", children: _jsx(Box, { h: "5", w: "1px", bg: "lightBlue.6" }) })] })), _jsx(IconButton, { "aria-label": t('ariaLabels.delete'), icon: _jsx(TrashIcon, {}), maxW: "fit-content", onClick: () => onClickDelete(room._id), disabled: isDeleteDisabled, px: "1", variant: "menuIconWithShadow" })] })] }) }));
|
|
12
12
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ControlledWizardI } from '../../interfaces';
|
|
2
2
|
import { PropsWithChildren } from 'react';
|
|
3
|
-
export declare function ControlledWizard<Key extends string = string>({ bodyWidth, steps, height, headerBlink, footerBlink, ...props }: ControlledWizardI<Key>): import("react/jsx-runtime").JSX.Element | null;
|
|
3
|
+
export declare function ControlledWizard<Key extends string = string>({ bodyWidth, steps, height, headerBlink, footerBlink, transition, ...props }: ControlledWizardI<Key>): import("react/jsx-runtime").JSX.Element | null;
|
|
4
4
|
export declare const WizardBodyPadding: ({ children }: PropsWithChildren) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -12,10 +12,12 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { Box, Flex, Stack } from '@chakra-ui/react';
|
|
14
14
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
15
|
+
import { usePrefersReducedMotion } from '../../hooks';
|
|
15
16
|
const MotionBox = motion.create(typeof Box);
|
|
16
17
|
export function ControlledWizard(_a) {
|
|
17
18
|
var _b, _c;
|
|
18
|
-
var { bodyWidth = '80%', steps, height = '700px', headerBlink, footerBlink } = _a, props = __rest(_a, ["bodyWidth", "steps", "height", "headerBlink", "footerBlink"]);
|
|
19
|
+
var { bodyWidth = '80%', steps, height = '700px', headerBlink, footerBlink, transition = 'crossfade' } = _a, props = __rest(_a, ["bodyWidth", "steps", "height", "headerBlink", "footerBlink", "transition"]);
|
|
20
|
+
const prefersReducedMotion = usePrefersReducedMotion();
|
|
19
21
|
const isKeyed = 'stepKey' in props;
|
|
20
22
|
const current = isKeyed
|
|
21
23
|
? (_b = steps.find((s) => s.key === props.stepKey)) !== null && _b !== void 0 ? _b : steps[0]
|
|
@@ -75,10 +77,47 @@ export function ControlledWizard(_a) {
|
|
|
75
77
|
transition: { duration: 0.18 },
|
|
76
78
|
},
|
|
77
79
|
};
|
|
80
|
+
// Directional (Typeform-style) body transition: horizontal slide + a subtle
|
|
81
|
+
// enter-flip (POC). Reduced motion collapses every transition to a plain fade.
|
|
82
|
+
const directionalBodyVariants = {
|
|
83
|
+
initial: { opacity: 0, x: 74, rotateY: 38 },
|
|
84
|
+
animate: {
|
|
85
|
+
opacity: 1,
|
|
86
|
+
x: 0,
|
|
87
|
+
rotateY: 0,
|
|
88
|
+
transition: { duration: 0.55, ease: [0.5, 0, 0.15, 1] },
|
|
89
|
+
},
|
|
90
|
+
exit: {
|
|
91
|
+
opacity: 0,
|
|
92
|
+
x: -74,
|
|
93
|
+
transition: { duration: 0.3, ease: [0.5, 0, 0.15, 1] },
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
const reducedVariants = {
|
|
97
|
+
initial: { opacity: 0 },
|
|
98
|
+
animate: { opacity: 1, transition: { duration: 0.2 } },
|
|
99
|
+
exit: { opacity: 0, transition: { duration: 0.15 } },
|
|
100
|
+
};
|
|
101
|
+
const effectiveHeaderVariants = prefersReducedMotion
|
|
102
|
+
? reducedVariants
|
|
103
|
+
: headerVariants;
|
|
104
|
+
const effectiveBodyVariants = prefersReducedMotion
|
|
105
|
+
? reducedVariants
|
|
106
|
+
: transition === 'directional'
|
|
107
|
+
? directionalBodyVariants
|
|
108
|
+
: bodyVariants;
|
|
109
|
+
const effectiveFooterVariants = prefersReducedMotion
|
|
110
|
+
? reducedVariants
|
|
111
|
+
: footerVariants;
|
|
78
112
|
if (!current)
|
|
79
113
|
return null;
|
|
80
|
-
return (_jsxs(Stack, { shadow: "md", h: height, bg: "white", overflow: "hidden", spacing: "0", children: [_jsx(AnimatePresence, { mode: "wait", children: _jsx(Box, { as: MotionBox, variants:
|
|
114
|
+
return (_jsxs(Stack, { shadow: "md", h: height, bg: "white", borderRadius: "10px", overflow: "hidden", spacing: "0", sx: { perspective: '1100px' }, children: [_jsx(AnimatePresence, { mode: "wait", children: _jsx(Box, { as: MotionBox, variants: effectiveHeaderVariants, initial: "initial", animate: "animate", exit: "exit", w: "full", flexShrink: 0, children: current.header }, `h-${motionKey}`) }), _jsx(AnimatePresence, { mode: "wait", children: _jsx(Box, { as: MotionBox, variants: effectiveBodyVariants, initial: "initial", animate: "animate", exit: "exit", flex: "1", display: "flex", minH: "0", children: _jsx(Flex, { direction: "column", flex: "auto", overflowY: "auto",
|
|
115
|
+
// overflowY:auto alone coerces overflow-x to auto too (CSS: one axis
|
|
116
|
+
// can't stay visible when the other scrolls), so a full-bleed step
|
|
117
|
+
// (e.g. the success header's negative mx) produced a horizontal
|
|
118
|
+
// scrollbar. Clip X explicitly — no step should scroll sideways.
|
|
119
|
+
overflowX: "hidden", minH: "0", px: "1", pb: "2", children: _jsx(Flex, { justify: "center", h: "full", w: "full", children: _jsx(Box, { w: bodyWidth, children: current.body({ setStep, setStepKey }) }) }) }) }, `b-${motionKey}`) }), current.footer && (_jsx(AnimatePresence, { mode: "wait", children: _jsx(Box, { as: MotionBox, variants: effectiveFooterVariants, initial: "initial", animate: "animate", exit: "exit", w: "full", flexShrink: 0, bg: "white", borderTopWidth: "1px", borderColor: "lightBlue.3", children: _jsx(Flex, { justify: "center", py: { base: '4', md: '5' }, children: _jsx(Box, { w: bodyWidth, children: current.footer }) }) }, `f-${motionKey}`) }))] }));
|
|
81
120
|
}
|
|
82
121
|
export const WizardBodyPadding = ({ children }) => {
|
|
83
|
-
return _jsx(Box, {
|
|
122
|
+
return (_jsx(Box, { pt: { base: '4', md: '8' }, pb: { base: '64px', md: '104px' }, children: children }));
|
|
84
123
|
};
|
|
@@ -6,16 +6,16 @@ import { fieldIcons, setOptionButtonStyles } from '../../helpers';
|
|
|
6
6
|
export const WizardInitialOptions = ({ form, title, callback, style = 'wizard', }) => {
|
|
7
7
|
const { control } = useFormContext();
|
|
8
8
|
const isWizard = style === 'wizard';
|
|
9
|
-
return (_jsxs(Stack, { spacing: "base",
|
|
9
|
+
return (_jsxs(Stack, { spacing: "base", children: [title && (_jsx(Text, { fontFamily: "secondary", fontSize: "sm", children: title })), _jsx(SimpleGrid, { columns: { base: 1, md: isWizard ? 2 : 1 }, spacing: { base: 1, md: isWizard ? 'base' : 1 }, children: form.map((field) => {
|
|
10
10
|
const { id, value, label, icon, description } = field;
|
|
11
11
|
return (_jsx(Controller, { control: control, name: id, defaultValue: value, render: ({ field: { value, onChange } }) => {
|
|
12
12
|
const iconSrc = icon
|
|
13
13
|
? fieldIcons[icon]
|
|
14
14
|
: undefined;
|
|
15
|
-
return (_jsxs(Container, { position: "relative", children: [value && (_jsx(Image, { src: CheckInCircle, alt: "check", w: "22px", h: "auto", position: "absolute", top: "-
|
|
15
|
+
return (_jsxs(Container, { position: "relative", boxShadow: "none", children: [value && (_jsx(Image, { src: CheckInCircle, alt: "check", w: "22px", h: "auto", position: "absolute", top: "-6px", left: "-6px", boxShadow: "lg", borderRadius: "full", zIndex: 1 })), _jsxs(Stack, Object.assign({ as: "button", p: "base", w: "full", border: "1px solid", borderColor: value ? 'green.1' : 'transparent', borderRadius: "8px", boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)", onClick: () => {
|
|
16
16
|
onChange(!value);
|
|
17
17
|
callback === null || callback === void 0 ? void 0 : callback(Object.assign(Object.assign({}, field), { value: !value }));
|
|
18
|
-
} }, setOptionButtonStyles(value), { children: [_jsxs(Flex, { gap: "2", w: "full", align: "center", children: [_jsx(Image, { src: iconSrc, alt: "icon", h: "16px", w: "auto" }), label && (_jsx(Text, { fontWeight: "
|
|
18
|
+
} }, setOptionButtonStyles(value), { children: [_jsxs(Flex, { gap: "2", w: "full", align: "center", children: [_jsx(Image, { src: iconSrc, alt: "icon", h: "16px", w: "auto" }), label && (_jsx(Text, { fontWeight: "600", fontSize: "sm", textTransform: "capitalize", fontFamily: "primary", children: label }))] }), _jsx(Text, { fontFamily: "secondary", textAlign: "left", children: description })] }))] }));
|
|
19
19
|
} }, id));
|
|
20
20
|
}) })] }));
|
|
21
21
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DynamicFormI } from '../../interfaces';
|
|
2
|
+
/**
|
|
3
|
+
* WizardSelectCard — the learn-the-basics wizard's selectable icon tile.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately its OWN component, separate from the legacy `WizardStepOptions`
|
|
6
|
+
* (which the deprecated HomeAssistant setup wizard still renders). Design:
|
|
7
|
+
* - fixed 80x80 square tiles that auto-fit across the row
|
|
8
|
+
* - a green outline + a corner check (top-left, poking out) when selected
|
|
9
|
+
* - a soft shadow, no gray container
|
|
10
|
+
*
|
|
11
|
+
* Same field-shape/RHF contract as the other option fields so the shared
|
|
12
|
+
* DynamicForm engine can route to it, but the legacy card is left as-is.
|
|
13
|
+
*/
|
|
14
|
+
export declare const WizardSelectCard: ({ form, title, callback }: DynamicFormI) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Center, Flex, Image, Stack, Text } from '@chakra-ui/react';
|
|
3
|
+
import { Controller, useFormContext } from 'react-hook-form';
|
|
4
|
+
import { CheckInCircle } from '../../assets/images';
|
|
5
|
+
import { fieldIcons, setOptionButtonStyles } from '../../helpers';
|
|
6
|
+
/**
|
|
7
|
+
* WizardSelectCard — the learn-the-basics wizard's selectable icon tile.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately its OWN component, separate from the legacy `WizardStepOptions`
|
|
10
|
+
* (which the deprecated HomeAssistant setup wizard still renders). Design:
|
|
11
|
+
* - fixed 80x80 square tiles that auto-fit across the row
|
|
12
|
+
* - a green outline + a corner check (top-left, poking out) when selected
|
|
13
|
+
* - a soft shadow, no gray container
|
|
14
|
+
*
|
|
15
|
+
* Same field-shape/RHF contract as the other option fields so the shared
|
|
16
|
+
* DynamicForm engine can route to it, but the legacy card is left as-is.
|
|
17
|
+
*/
|
|
18
|
+
export const WizardSelectCard = ({ form, title, callback }) => {
|
|
19
|
+
const { control } = useFormContext();
|
|
20
|
+
return (_jsxs(Stack, { spacing: "base", children: [title && (_jsx(Text, { fontFamily: "secondary", fontSize: "sm", children: title })), _jsx(Flex, { flexWrap: "wrap", gap: "base", children: form.map((field) => {
|
|
21
|
+
const { id, value, label, icon } = field;
|
|
22
|
+
return (_jsx(Controller, { control: control, name: id, defaultValue: value, render: ({ field: { value, onChange } }) => {
|
|
23
|
+
const iconSrc = icon
|
|
24
|
+
? fieldIcons[icon]
|
|
25
|
+
: undefined;
|
|
26
|
+
return (_jsxs(Box, { position: "relative", boxSize: "80px", children: [value && (_jsx(Image, { src: CheckInCircle, alt: "check", w: "22px", h: "auto", position: "absolute", top: "-6px", left: "-6px", boxShadow: "lg", borderRadius: "full", zIndex: 1 })), _jsxs(Stack, Object.assign({ as: "button", py: "1", spacing: "1", w: "full", h: "full", justify: "center", border: "1px solid", borderColor: value ? 'green.1' : 'transparent', borderRadius: "8px", boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)", onClick: () => {
|
|
27
|
+
onChange(!value);
|
|
28
|
+
callback === null || callback === void 0 ? void 0 : callback(Object.assign(Object.assign({}, field), { value: !value }));
|
|
29
|
+
} }, setOptionButtonStyles(value), { children: [_jsx(Center, { h: "38px", flexShrink: 0, w: "100%", alignItems: "flex-end", children: _jsx(Image, { src: iconSrc, alt: "icon", boxSize: "36px", objectFit: "contain" }) }), _jsx(Flex, { h: "26px", flexShrink: 0, px: "1", pt: "2px", w: "100%", justify: "center", align: "center", children: _jsx(Text, { fontFamily: "primary", fontSize: "11px", fontWeight: "500", color: "gray.3", textTransform: "uppercase", textAlign: "center", lineHeight: "1", noOfLines: 2, children: label }) })] }))] }));
|
|
30
|
+
} }, id));
|
|
31
|
+
}) })] }));
|
|
32
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { WizardStepPromptI } from '../../interfaces';
|
|
2
|
+
/**
|
|
3
|
+
* WizardStepPrompt — the conversational (Typeform) step header for the setup
|
|
4
|
+
* wizard: a step marker, Homi's bold prompt, and an optional supporting line.
|
|
5
|
+
* Left-aligned, white. Structure is fixed; the copy is Homi-authored (passed
|
|
6
|
+
* in as props), never hardcoded.
|
|
7
|
+
*
|
|
8
|
+
* Baked-in type treatment (from the approved POC): 30px semibold Poppins
|
|
9
|
+
* headline, 16px Assistant subtitle in body-copy grey with tight line-height.
|
|
10
|
+
*/
|
|
11
|
+
export declare const WizardStepPrompt: ({ marker, title, subtitle, }: WizardStepPromptI) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Stack, Text } from '@chakra-ui/react';
|
|
3
|
+
/**
|
|
4
|
+
* WizardStepPrompt — the conversational (Typeform) step header for the setup
|
|
5
|
+
* wizard: a step marker, Homi's bold prompt, and an optional supporting line.
|
|
6
|
+
* Left-aligned, white. Structure is fixed; the copy is Homi-authored (passed
|
|
7
|
+
* in as props), never hardcoded.
|
|
8
|
+
*
|
|
9
|
+
* Baked-in type treatment (from the approved POC): 30px semibold Poppins
|
|
10
|
+
* headline, 16px Assistant subtitle in body-copy grey with tight line-height.
|
|
11
|
+
*/
|
|
12
|
+
export const WizardStepPrompt = ({ marker, title, subtitle, }) => (_jsxs(Stack, { spacing: "3", pt: "8", children: [marker != null && (_jsx(Text, { fontFamily: "primary", fontWeight: "semibold", fontSize: "sm", color: "blue.2", children: marker })), _jsx(Text, { fontFamily: "primary", fontWeight: "semibold", fontSize: "28px", lineHeight: "1.15", color: "gray.4",
|
|
13
|
+
// text-wrap: balance evens out line lengths on the (mind-authored,
|
|
14
|
+
// variable-length) headline — no hand-tuning needed. Graceful no-op
|
|
15
|
+
// on older browsers.
|
|
16
|
+
sx: { textWrap: 'balance' }, children: title }), subtitle && (_jsx(Text, { fontFamily: "secondary", fontSize: "16px", lineHeight: "1.3", color: "gray.3",
|
|
17
|
+
// text-wrap: pretty prevents widows/orphans on the supporting line.
|
|
18
|
+
// Required for dynamic copy: the mind authors this, so CSS (not
|
|
19
|
+
// sentence-tuning) has to keep every generated variant clean.
|
|
20
|
+
sx: { textWrap: 'pretty' }, children: subtitle }))] }));
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { WizardSuccessHeaderI } from '../../interfaces';
|
|
2
|
-
export declare const WizardSuccessHeader: ({ title, subtitle, address, name, image, }: WizardSuccessHeaderI) => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const WizardSuccessHeader: ({ bg, title, subtitle, address, name, image, }: WizardSuccessHeaderI) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,8 +4,8 @@ import { Center, Show, Stack, Text, Box, Flex, Container, Image, } from '@chakra
|
|
|
4
4
|
import { ContainerHeader, Logo } from '../../components';
|
|
5
5
|
import { randomImages } from '../../utils';
|
|
6
6
|
import { useMyHomeCard } from '../../hooks';
|
|
7
|
-
export const WizardSuccessHeader = ({ title, subtitle, address, name, image, }) => {
|
|
8
|
-
return (_jsx(Center, { w: "full", bg:
|
|
7
|
+
export const WizardSuccessHeader = ({ bg = 'lightGreen.1', title, subtitle, address, name, image, }) => {
|
|
8
|
+
return (_jsx(Center, { w: "full", bg: bg, minH: "212px", children: _jsxs(Stack, { w: "full", spacing: "0", align: "center", children: [_jsx(Show, { below: "md", children: _jsx(Logo, {}) }), _jsxs(Flex, { gap: "8", w: "80%", align: "center", pt: { base: '0', md: '8' }, direction: { base: 'column', md: 'row' }, children: [_jsx(Box, { maxW: "205px", children: _jsx(WizardHomeCard, { address: address, name: name, image: image }) }), _jsxs(Stack, { spacing: "2", children: [_jsxs(Text, { fontSize: "xl", fontWeight: "semibold", color: "green.1", children: [t('ariaLabels.success'), "!"] }), _jsx(Text, { fontSize: { base: '2xl', md: '3xl' }, lineHeight: "shorter", children: title }), subtitle && _jsx(Text, { fontFamily: "secondary", children: subtitle }), _jsx(Show, { below: "md", children: _jsx(Box, { h: "6" }) })] })] })] }) }));
|
|
9
9
|
};
|
|
10
10
|
const WizardHomeCard = ({ address, name, image, }) => {
|
|
11
11
|
const { imageUrl } = useMyHomeCard({
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export * from './ControlledWizard';
|
|
2
2
|
export * from './WizardInitialOptions';
|
|
3
3
|
export * from './WizardStepOptions';
|
|
4
|
+
export * from './WizardSelectCard';
|
|
4
5
|
export * from './WizardStepGoal';
|
|
6
|
+
export * from './WizardStepPrompt';
|
|
5
7
|
export * from './WizardSteps';
|
|
6
8
|
export * from './WizardSuccessHeader';
|
|
7
9
|
export * from './WizardTextHeader';
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export * from './ControlledWizard';
|
|
2
2
|
export * from './WizardInitialOptions';
|
|
3
3
|
export * from './WizardStepOptions';
|
|
4
|
+
export * from './WizardSelectCard';
|
|
4
5
|
export * from './WizardStepGoal';
|
|
6
|
+
export * from './WizardStepPrompt';
|
|
5
7
|
export * from './WizardSteps';
|
|
6
8
|
export * from './WizardSuccessHeader';
|
|
7
9
|
export * from './WizardTextHeader';
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Paint, Receipt, Barcode, GoldenBars, Heart, Rating, Notes, Book, ImgIcon, Registry, ShareWith, Calendar, Contacts, Tools2, Billing, Calc, CheckPen, GenericField, Price, Battery2, Co2, Detector, Electricity, Sprinkler, SixtyEight, DropCalendar, MobileDrop, SolarPanel, Calendar2, Drop, Wind, Foundation, Heater, Light, Plate, Pool, Closet, Structure, GlassWater, Roof2, Target, CircleFace, Pressure, WindowCleaning, RoofCleaning, HolidayLights, GutterCleaning, HomeCleaning, HouseWashing, PressureWasher, BookOpened, Company2, AADisease, AADressing, AALawn, AAMowing, AAWeed, AApest, FilterSize, Wallet, Fire, Plant, LifePreserver, BOCFilter, BOCGreen, BOCRenovation, BOCRepair, BOCService, BOCWarranty, MRBoxes, MRClock, MRCouch, MRHand, MRMakeReady, MRRental, Cleaning, GearTime, YellowFolder, Warranty, Flower, Jacuzzi, MagnifyingGlassPerson, TPlumberDrain, TPlumberEmergency, TPlumberExcavation, TPlumberGeneral, TPlumberWater, PNMSlab, PNMTreatment, PNMWaterHeater, HMCeilingFan, HMDrill, HMFaucet, HMHanging, HMSecurity, HMTV, IlHome, IlHvac, IlPower, IlSolar, IlTools } from '../../assets/images';
|
|
2
|
+
// Relative (not the @/ alias): tsc-alias does not rewrite direct static-asset
|
|
3
|
+
// imports, so an @/ alias here survives into dist unresolved and breaks Vite.
|
|
4
|
+
import PdfIcon from '../../assets/images/pdf-icon.svg';
|
|
2
5
|
import { CookTop, Dishwasher, Dryer, Freezer, Fridge, Hood, Microwave, Oven, Range, Washer, tankWater, tanklessWater, } from '../../assets/images/appliances';
|
|
3
6
|
import { BatterySmokeDetector, Co2Detector, FireExtinguisher, Furnace, HeatPump, InWall, LandscapeLights, Other, SprinklerSystem, SprinklerSystem2, WiredSmokeDetector, } from '../../assets/images/home-assistant';
|
|
4
7
|
export const fieldIcons = {
|
|
@@ -67,6 +70,7 @@ export const fieldIcons = {
|
|
|
67
70
|
'tank-water': tankWater,
|
|
68
71
|
'tankless-water': tanklessWater,
|
|
69
72
|
'wired-smoke-detector': WiredSmokeDetector,
|
|
73
|
+
pdf: PdfIcon,
|
|
70
74
|
barcode: Barcode,
|
|
71
75
|
battery: Battery2,
|
|
72
76
|
billing: Billing,
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
|
+
// Extensions we can render directly as an <img> thumbnail. A file is treated as
|
|
3
|
+
// an image by its extension rather than by bucket name: image uploads land in
|
|
4
|
+
// `homefile-images` in production but in `homefile-test` in non-prod, so gating
|
|
5
|
+
// on a single bucket name silently hid thumbnails outside production. Every
|
|
6
|
+
// storage bucket is public-read and the object key is `<fileName>.<extension>`,
|
|
7
|
+
// so the public URL is derivable from the file's own stored bucketName.
|
|
8
|
+
const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'gif'];
|
|
2
9
|
export const useDisplayFiles = ({ files, title }) => {
|
|
3
10
|
const [fileExtension, updateFileExtension] = useState("");
|
|
4
11
|
const [imageUrl, updateImageUrl] = useState("");
|
|
5
12
|
useEffect(() => {
|
|
13
|
+
var _a;
|
|
6
14
|
if (files && files.length > 0) {
|
|
7
15
|
const firstFile = files[0];
|
|
8
16
|
updateFileExtension(firstFile.extension);
|
|
9
|
-
|
|
17
|
+
const ext = `${(_a = firstFile.extension) !== null && _a !== void 0 ? _a : ""}`.toLowerCase();
|
|
18
|
+
if (firstFile.bucketName && firstFile.fileName && IMAGE_EXTENSIONS.includes(ext)) {
|
|
10
19
|
const storageUrl = import.meta.env.VITE_STORAGE_URL;
|
|
11
|
-
updateImageUrl(`${storageUrl}/${firstFile
|
|
20
|
+
updateImageUrl(`${storageUrl}/${firstFile.bucketName}/${firstFile.fileName}.${firstFile.extension}`);
|
|
12
21
|
}
|
|
13
22
|
else {
|
|
14
23
|
updateImageUrl("");
|
|
@@ -76,8 +76,14 @@ export const useAddressStored = ({ proxy = addressProxy, setIsDirty, }) => {
|
|
|
76
76
|
};
|
|
77
77
|
const handleCityAndState = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
78
78
|
const { city, state } = yield getAddress(fields.zip);
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
// Only fill from the zip lookup when it actually returns values. A failed or
|
|
80
|
+
// empty lookup must NOT wipe city/state we already have — e.g. values seeded
|
|
81
|
+
// from a saved home when opening the Edit Home panel (the lookup for an
|
|
82
|
+
// existing zip can come back empty and was blanking the fields).
|
|
83
|
+
if (city)
|
|
84
|
+
fields.city = city;
|
|
85
|
+
if (state)
|
|
86
|
+
fields.state = state;
|
|
81
87
|
});
|
|
82
88
|
const handleCleanProxy = () => proxy.setFields({
|
|
83
89
|
apartmentNumber: '',
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -32,5 +32,7 @@ export * from './useComponentStyles';
|
|
|
32
32
|
export * from './useCustomToast';
|
|
33
33
|
export * from './useFileUploader';
|
|
34
34
|
export * from './useImage';
|
|
35
|
+
export * from './useDirtySave';
|
|
35
36
|
export * from './useWindowDimensions';
|
|
36
37
|
export * from './valueMonitor';
|
|
38
|
+
export * from './motion';
|
package/dist/hooks/index.js
CHANGED
|
@@ -32,5 +32,7 @@ export * from './useComponentStyles';
|
|
|
32
32
|
export * from './useCustomToast';
|
|
33
33
|
export * from './useFileUploader';
|
|
34
34
|
export * from './useImage';
|
|
35
|
+
export * from './useDirtySave';
|
|
35
36
|
export * from './useWindowDimensions';
|
|
36
37
|
export * from './valueMonitor';
|
|
38
|
+
export * from './motion';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Counts a number up to `target` over `duration` ms — a direct port of the
|
|
3
|
+
* POC's implementation: `duration = 1100ms`, ease-out-cubic, a
|
|
4
|
+
* `requestAnimationFrame` loop, and `toLocaleString()` formatting. Respects
|
|
5
|
+
* reduced motion (jumps straight to the target, no animation). Returns the
|
|
6
|
+
* formatted display string.
|
|
7
|
+
*
|
|
8
|
+
* Narrow utility — NOT a general animation engine (Eli's over-engineering flag).
|
|
9
|
+
*/
|
|
10
|
+
export declare const useCountUp: (target: number, duration?: number, easing?: (p: number) => number) => string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { usePrefersReducedMotion } from './usePrefersReducedMotion';
|
|
3
|
+
// ease-out-cubic (POC): 1 - (1 - p)^3
|
|
4
|
+
const easeOutCubic = (p) => 1 - Math.pow(1 - p, 3);
|
|
5
|
+
/**
|
|
6
|
+
* Counts a number up to `target` over `duration` ms — a direct port of the
|
|
7
|
+
* POC's implementation: `duration = 1100ms`, ease-out-cubic, a
|
|
8
|
+
* `requestAnimationFrame` loop, and `toLocaleString()` formatting. Respects
|
|
9
|
+
* reduced motion (jumps straight to the target, no animation). Returns the
|
|
10
|
+
* formatted display string.
|
|
11
|
+
*
|
|
12
|
+
* Narrow utility — NOT a general animation engine (Eli's over-engineering flag).
|
|
13
|
+
*/
|
|
14
|
+
export const useCountUp = (target, duration = 1100, easing = easeOutCubic) => {
|
|
15
|
+
const prefersReducedMotion = usePrefersReducedMotion();
|
|
16
|
+
const [value, setValue] = useState(prefersReducedMotion ? target : 0);
|
|
17
|
+
const rafRef = useRef(undefined);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (prefersReducedMotion) {
|
|
20
|
+
setValue(target);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
let start = null;
|
|
24
|
+
const step = (timestamp) => {
|
|
25
|
+
if (start === null)
|
|
26
|
+
start = timestamp;
|
|
27
|
+
const progress = Math.min((timestamp - start) / duration, 1);
|
|
28
|
+
setValue(Math.round(target * easing(progress)));
|
|
29
|
+
if (progress < 1)
|
|
30
|
+
rafRef.current = requestAnimationFrame(step);
|
|
31
|
+
};
|
|
32
|
+
rafRef.current = requestAnimationFrame(step);
|
|
33
|
+
return () => {
|
|
34
|
+
if (rafRef.current)
|
|
35
|
+
cancelAnimationFrame(rafRef.current);
|
|
36
|
+
};
|
|
37
|
+
}, [target, duration, easing, prefersReducedMotion]);
|
|
38
|
+
return value.toLocaleString();
|
|
39
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single house hook for JS / framer-motion reduced-motion gating.
|
|
3
|
+
*
|
|
4
|
+
* A thin wrapper over framer-motion's built-in `useReducedMotion()` (already a
|
|
5
|
+
* dependency, SSR-safe, purpose-built). framer-motion returns `boolean | null`
|
|
6
|
+
* (`null` before it can read the media query); we coerce to a definite boolean,
|
|
7
|
+
* defaulting to motion-allowed (`false`).
|
|
8
|
+
*
|
|
9
|
+
* NOTE: the 3 existing CSS `@media (prefers-reduced-motion: reduce)` blocks
|
|
10
|
+
* (Stars / Birds / HomeMonitorButton) are a DIFFERENT mechanism (they gate CSS
|
|
11
|
+
* animations declaratively) and stay as-is — this hook is only for JS-driven
|
|
12
|
+
* (framer-motion) motion. One hook; no fourth reduced-motion implementation.
|
|
13
|
+
*/
|
|
14
|
+
export declare const usePrefersReducedMotion: () => boolean;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useReducedMotion } from 'framer-motion';
|
|
2
|
+
/**
|
|
3
|
+
* The single house hook for JS / framer-motion reduced-motion gating.
|
|
4
|
+
*
|
|
5
|
+
* A thin wrapper over framer-motion's built-in `useReducedMotion()` (already a
|
|
6
|
+
* dependency, SSR-safe, purpose-built). framer-motion returns `boolean | null`
|
|
7
|
+
* (`null` before it can read the media query); we coerce to a definite boolean,
|
|
8
|
+
* defaulting to motion-allowed (`false`).
|
|
9
|
+
*
|
|
10
|
+
* NOTE: the 3 existing CSS `@media (prefers-reduced-motion: reduce)` blocks
|
|
11
|
+
* (Stars / Birds / HomeMonitorButton) are a DIFFERENT mechanism (they gate CSS
|
|
12
|
+
* animations declaratively) and stay as-is — this hook is only for JS-driven
|
|
13
|
+
* (framer-motion) motion. One hook; no fourth reduced-motion implementation.
|
|
14
|
+
*/
|
|
15
|
+
export const usePrefersReducedMotion = () => { var _a; return (_a = useReducedMotion()) !== null && _a !== void 0 ? _a : false; };
|
|
@@ -4,9 +4,12 @@ interface UseRoomHeaderI {
|
|
|
4
4
|
onSubmit: (room: RoomItemI) => void;
|
|
5
5
|
room: RoomItemI;
|
|
6
6
|
roomAmount: string;
|
|
7
|
+
roomEstimatedAmount?: string;
|
|
7
8
|
}
|
|
8
|
-
export declare const useRoomHeader: ({ onSubmit, room: { _id, name, type, active, home }, roomAmount, }: UseRoomHeaderI) => {
|
|
9
|
+
export declare const useRoomHeader: ({ onSubmit, room: { _id, name, type, active, home }, roomAmount, roomEstimatedAmount, }: UseRoomHeaderI) => {
|
|
9
10
|
formattedAmount: string;
|
|
11
|
+
formattedEstimatedAmount: string;
|
|
12
|
+
hasEstimate: boolean;
|
|
10
13
|
handleChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
11
14
|
handleClick: () => void;
|
|
12
15
|
inputValue: string | null;
|