@4alldigital/foundation-ui--core 3.10.1 → 3.11.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.
Files changed (129) hide show
  1. package/package.json +2 -2
  2. package/src/components/Accordion/Accordion.tsx +3 -1
  3. package/src/components/AddressForm/AddressForm.tsx +4 -1
  4. package/src/components/AnimationCounter/AnimationCounter.tsx +3 -1
  5. package/src/components/AnimationSet1/AnimationSet1.tsx +3 -1
  6. package/src/components/Authenticator/Authenticator.tsx +2 -0
  7. package/src/components/Avatar/Avatar.tsx +2 -0
  8. package/src/components/Banner/Banner.tsx +1 -1
  9. package/src/components/Blockquote/Blockquote.tsx +1 -1
  10. package/src/components/Button/Button.tsx +1 -2
  11. package/src/components/ButtonDeprecated/Button.tsx +3 -1
  12. package/src/components/ButtonGroup/ButtonGroup.tsx +1 -1
  13. package/src/components/Card/Card.types.ts +3 -3
  14. package/src/components/Card/types/CardBasic/CardBasic.tsx +44 -133
  15. package/src/components/Card/types/CardData/CardData.tsx +3 -1
  16. package/src/components/Card/types/CardHorz/CardHorz.tsx +3 -1
  17. package/src/components/Card/types/CardIcon/CardIcon.tsx +3 -1
  18. package/src/components/Card/types/CardMedia/CardMedia.tsx +3 -1
  19. package/src/components/Card/types/CardVideo/CardVideo.tsx +3 -1
  20. package/src/components/Carousel/Carousel.tsx +3 -1
  21. package/src/components/Cart/Cart.tsx +15 -5
  22. package/src/components/Checkbox/Checkbox.tsx +4 -4
  23. package/src/components/Chip/Chip.tsx +3 -1
  24. package/src/components/Collapsible/Collapsible.tsx +1 -1
  25. package/src/components/Container/Container.tsx +1 -1
  26. package/src/components/ContentPageLayout/ContentPageLayout.tsx +1 -1
  27. package/src/components/ContentRowsLayout/ContentRowsLayout.tsx +1 -1
  28. package/src/components/Copy/Copy.tsx +1 -1
  29. package/src/components/DisplayHeading/DisplayHeading.tsx +1 -1
  30. package/src/components/ErrorBoundary/ErrorBoundary.tsx +44 -0
  31. package/src/components/ErrorBoundary/ErrorBoundary.types.ts +12 -0
  32. package/src/components/ErrorBoundary/index.ts +2 -0
  33. package/src/components/FileUpload/FileUpload.tsx +3 -1
  34. package/src/components/Footer/Footer.tsx +3 -1
  35. package/src/components/Form/Form.tsx +10 -4
  36. package/src/components/Form/Form.types.ts +4 -3
  37. package/src/components/FormField/FormField.tsx +12 -10
  38. package/src/components/FormSelect/FormSelect.tsx +2 -2
  39. package/src/components/FormSelect/FormSelect.types.ts +5 -11
  40. package/src/components/FullScreenVideoModal/FullScreenVideoModal.tsx +3 -1
  41. package/src/components/Header/Header.tsx +27 -9
  42. package/src/components/Heading/Heading.tsx +12 -19
  43. package/src/components/Heading/Heading.types.ts +1 -1
  44. package/src/components/Hero/Hero.tsx +1 -1
  45. package/src/components/Hr/Hr.tsx +1 -1
  46. package/src/components/HtmlContent/HtmlContent.tsx +1 -1
  47. package/src/components/Image/Image.tsx +1 -1
  48. package/src/components/Label/Label.tsx +1 -1
  49. package/src/components/Link/Link.tsx +3 -1
  50. package/src/components/List/List.tsx +1 -1
  51. package/src/components/ListItem/ListItem.tsx +3 -1
  52. package/src/components/Loader/Loader.tsx +1 -1
  53. package/src/components/Logo/Logo.tsx +1 -1
  54. package/src/components/Menu/Menu.tsx +1 -1
  55. package/src/components/Notice/Notice.tsx +3 -1
  56. package/src/components/OTPInput/OTPInput.tsx +4 -2
  57. package/src/components/PanelCards/PanelCards.tsx +3 -1
  58. package/src/components/PanelCustom/PanelCustom.tsx +1 -1
  59. package/src/components/PanelFeature/PanelFeature.tsx +1 -1
  60. package/src/components/PanelHero/PanelHero.tsx +3 -1
  61. package/src/components/PanelIntro/PanelIntro.tsx +3 -1
  62. package/src/components/PanelProduct/PanelProduct.tsx +1 -1
  63. package/src/components/PanelScroller/PanelScroller.tsx +3 -1
  64. package/src/components/PanelVideo/PanelVideo.tsx +3 -1
  65. package/src/components/ProductCard/ProductCard.tsx +2 -0
  66. package/src/components/ProductDetail/ProductDetail.tsx +2 -0
  67. package/src/components/Radio/Radio.tsx +1 -1
  68. package/src/components/Schedule/Schedule.tsx +2 -2
  69. package/src/components/ScheduleItem/ScheduleItem.tsx +2 -0
  70. package/src/components/Screen/Screen.tsx +1 -1
  71. package/src/components/ShadcnCarousel/ShadcnCarousel.tsx +2 -0
  72. package/src/components/Share/Share.tsx +2 -0
  73. package/src/components/SubscriptionManager/SubscriptionManager.tsx +3 -1
  74. package/src/components/Switch/Switch.tsx +1 -1
  75. package/src/components/Tabs/Tabs.tsx +3 -1
  76. package/src/components/TextInput/TextInput.tsx +6 -3
  77. package/src/components/TextInput/TextInput.types.ts +4 -55
  78. package/src/components/VariantSelector/VariantSelector.tsx +2 -0
  79. package/src/components/Video/Video.tsx +14 -13
  80. package/src/components/VisitUs/VisitUs.tsx +2 -0
  81. package/src/components/index.ts +7 -0
  82. package/src/context/Amplify/index.tsx +2 -1
  83. package/src/context/App/index.tsx +2 -0
  84. package/src/context/Cart/index.tsx +2 -0
  85. package/src/context/ExampleContext/index.tsx +3 -1
  86. package/src/context/Theme/index.tsx +2 -0
  87. package/src/context/index.ts +2 -0
  88. package/src/features/Search/Search.tsx +2 -0
  89. package/src/features/Search/config/config-helper.ts +2 -0
  90. package/src/features/Search/views/Layout/Layout.tsx +1 -1
  91. package/src/features/Search/views/MultiCheckboxFacet/MultiCheckboxFacet.tsx +3 -1
  92. package/src/features/Search/views/Paging/Paging.tsx +3 -1
  93. package/src/features/Search/views/PagingInfo/PagingInfo.tsx +1 -1
  94. package/src/features/Search/views/Result/Result.tsx +1 -1
  95. package/src/features/Search/views/Results/Results.tsx +1 -1
  96. package/src/features/Search/views/ResultsPerPage/ResultsPerPage.tsx +3 -1
  97. package/src/features/Search/views/SearchBox/SearchBox.tsx +3 -1
  98. package/src/features/Search/views/SingleLinksFacet/SingleLinksFacet.tsx +3 -1
  99. package/src/features/Search/views/SingleSelectFacet/SingleSelectFacet.tsx +3 -1
  100. package/src/features/Search/views/Sorting/Sorting.tsx +3 -1
  101. package/src/features/index.ts +2 -0
  102. package/src/forms/ForgotPasswordForm/ForgotPasswordForm.types.ts +1 -1
  103. package/src/forms/LoginForm/LoginForm.stories.tsx +1 -1
  104. package/src/forms/LoginForm/LoginForm.tsx +7 -5
  105. package/src/forms/LoginForm/LoginForm.types.ts +7 -1
  106. package/src/forms/PromoCodeForm/PromoCodeForm.types.ts +1 -1
  107. package/src/forms/RegisterForm/RegisterForm.stories.tsx +1 -1
  108. package/src/forms/RegisterForm/RegisterForm.tsx +7 -10
  109. package/src/forms/RegisterForm/RegisterForm.types.ts +4 -2
  110. package/src/forms/ResetPasswordAuthForm/ResetPasswordAuthForm.types.ts +1 -1
  111. package/src/forms/ResetPasswordForm/ResetPasswordForm.types.ts +1 -1
  112. package/src/forms/index.ts +2 -0
  113. package/src/hooks/index.ts +2 -0
  114. package/src/hooks/useLanguage.ts +2 -0
  115. package/src/hooks/useTheme.ts +3 -1
  116. package/src/index.ts +2 -0
  117. package/src/templates/AccountScreen/AccountScreen.tsx +2 -0
  118. package/src/templates/ChallengeScreen/ChallengeScreen.tsx +2 -0
  119. package/src/templates/MenuScreen/MenuScreen.tsx +2 -0
  120. package/src/templates/OrderDetailScreen/OrderDetailScreen.tsx +2 -0
  121. package/src/templates/OrdersHistoryScreen/OrdersHistoryScreen.tsx +2 -0
  122. package/src/templates/PasswordResetScreen/PasswordResetScreen.types.ts +1 -1
  123. package/src/templates/ProductListScreen/ProductListScreen.tsx +2 -0
  124. package/src/templates/PurchaseConfirmationScreen/PurchaseConfirmationScreen.tsx +2 -0
  125. package/src/templates/SubscriptionScreen/SubscriptionScreen.tsx +2 -0
  126. package/src/templates/WorkoutScreen/WorkoutScreen.tsx +3 -1
  127. package/src/templates/index.ts +2 -0
  128. package/src/utils/htmlParser/index.tsx +11 -5
  129. package/src/utils/index.ts +1 -1
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React, { useEffect, useRef } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { useForm, FormProvider } from 'react-hook-form';
4
6
  import { Props } from './Form.types';
5
7
  import Button from '../Button';
@@ -39,14 +41,18 @@ const Form = ({
39
41
  }
40
42
  }, [initialValues]);
41
43
 
44
+ // Use a ref to always call the latest onChange without resubscribing watch()
45
+ const onChangeRef = useRef(onChange);
46
+ onChangeRef.current = onChange;
47
+
42
48
  useEffect(() => {
43
- if (onChange && typeof onChange === 'function') {
49
+ if (onChangeRef.current) {
44
50
  const subscription = methods.watch((value, event) => {
45
- onChange(value, event);
51
+ onChangeRef.current?.(value, event);
46
52
  });
47
53
  return () => subscription.unsubscribe();
48
54
  }
49
- }, []);
55
+ }, [methods]);
50
56
 
51
57
  const submitClasses = cx({ 'sr-only': hideSubmit });
52
58
 
@@ -1,3 +1,4 @@
1
+ import { type FieldValues } from 'react-hook-form';
1
2
  import { BTN_TYPES, BTN_VARIANTS } from '../Button/Button.types';
2
3
 
3
4
  export interface Props {
@@ -8,9 +9,9 @@ export interface Props {
8
9
  /** Disable form inputs validation. */
9
10
  noValidate?: boolean;
10
11
  /** The submit handler that will fire once the form is submitted. */
11
- onSubmit?: (values: any, event?: any) => void;
12
+ onSubmit?: (values: FieldValues, event?: React.BaseSyntheticEvent) => void;
12
13
  /** The on change handler that will fire once the form is updated. */
13
- onChange?: (values: any, event?: any) => void;
14
+ onChange?: (values: FieldValues, event?: { name?: string; type?: string }) => void;
14
15
  /** The label for the submit button. */
15
16
  submit?: string;
16
17
  /** Hide submit button. */
@@ -20,7 +21,7 @@ export interface Props {
20
21
  /** Unique form submit button id. */
21
22
  submitID?: string;
22
23
  /** Initial form values. */
23
- initialValues?: { [k: string]: any };
24
+ initialValues?: FieldValues;
24
25
  /** The submit button variant. */
25
26
  submitVariant?: BTN_VARIANTS;
26
27
  /** The form is boxed. */
@@ -1,6 +1,8 @@
1
+ "use client";
2
+
1
3
  import { useState } from 'react';
2
4
  import { Controller, useFormContext } from 'react-hook-form';
3
- import cx from 'classnames';
5
+ import { clsx as cx } from 'clsx';
4
6
  import { FIELD_TYPES, Props } from './FormField.types';
5
7
  import TextInput from '../TextInput';
6
8
  import { InputType } from '../TextInput/TextInput.types';
@@ -57,16 +59,13 @@ const FormField = ({
57
59
  return (
58
60
  <div className="relative">
59
61
  <span
60
- className="absolute right-0 top-0 flex items-center justify-center h-full z-10 pr-2"
61
- onClick={() => setPasswordShow((pS) => !pS)}
62
- role="button"
63
- tabIndex={0}>
62
+ className="absolute right-0 top-0 flex items-center justify-center h-full z-10 pr-2">
64
63
  {passwordShowStatus ? (
65
- <button type="button" aria-label={hidePasswordLabel} className={cx(btnClass, 'form-icon')}>
64
+ <button type="button" aria-label={hidePasswordLabel} className={cx(btnClass, 'form-icon')} onClick={() => setPasswordShow(false)}>
66
65
  <Icon name="mdi:eye-off" size={24} />
67
66
  </button>
68
67
  ) : (
69
- <button type="button" aria-label={showPasswordLabel} className={cx(btnClass, 'form-icon')}>
68
+ <button type="button" aria-label={showPasswordLabel} className={cx(btnClass, 'form-icon')} onClick={() => setPasswordShow(true)}>
70
69
  <Icon name="mdi:eye" size={24} />
71
70
  </button>
72
71
  )}
@@ -213,6 +212,9 @@ const FormField = ({
213
212
  // TODO: HELPER CLASSES : data-[invalid=true]:bg-error data-[valid]:bg-success
214
213
  const showLabel = label && type !== FIELD_TYPES.CHECKBOX;
215
214
  const labelWithAsterisk = required || Boolean(validations.required) ? `${label} *` : label;
215
+ const descriptionId = description ? `${name}-description` : undefined;
216
+ const errorId = errors[name] ? `${name}-error` : undefined;
217
+ const ariaDescribedBy = [descriptionId, errorId].filter(Boolean).join(' ') || undefined;
216
218
  return (
217
219
  <div className="relative">
218
220
  <div data-testid="FormField" className="mb-4">
@@ -229,19 +231,19 @@ const FormField = ({
229
231
  control={control}
230
232
  rules={validations}
231
233
  render={({ field }) => {
232
- return renderType(type, field);
234
+ return renderType(type, { ...field, 'aria-describedby': ariaDescribedBy, 'aria-invalid': !!errors[name] });
233
235
  }}
234
236
  />
235
237
  </div>
236
238
  {description && (
237
- <div className="formfield-description">
239
+ <div id={descriptionId} className="formfield-description">
238
240
  <Copy size={COPY_SIZE.SMALL} className="mb-0">
239
241
  {description}
240
242
  </Copy>
241
243
  </div>
242
244
  )}
243
245
  {errors[name] && (
244
- <div className="formfield-error text-error italic">
246
+ <div id={errorId} role="alert" className="formfield-error text-error italic">
245
247
  <Copy size={COPY_SIZE.SMALL} className="mb-0">
246
248
  {errors?.[name]?.message?.toString()}
247
249
  </Copy>
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './FormSelect.types';
4
4
  import Icon from '../Icon';
5
5
 
6
6
  const FormSelect = React.forwardRef(function MyInput(
7
- { id, name, options, required, className, disabled, ...rest }: Props,
7
+ { id, name, options, required, className, disabled, label: _label, iconColor: _iconColor, itemIcon: _itemIcon, wrapperStyles: _wrapperStyles, selectIconUp: _selectIconUp, selectIconDown: _selectIconDown, error: _error, ...rest }: Props,
8
8
  ref: any,
9
9
  ) {
10
10
  if (!options) {
@@ -8,17 +8,11 @@ export type OptionsType = {
8
8
  disabled?: boolean;
9
9
  };
10
10
 
11
- export interface Props {
11
+ export interface Props extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'onChange'> {
12
12
  /** The selected option's value. */
13
13
  options?: Array<OptionsType>;
14
- /** Unique id for the field, required for a11y. */
15
- id: string;
16
14
  /** The select visible default label value. */
17
15
  label?: string | React.ReactNode[];
18
- /** Is field required. */
19
- required?: boolean;
20
- /** Additional classes. */
21
- className?: string;
22
16
  /** Dropdown icon color. */
23
17
  iconColor?: string;
24
18
  /** Dropdown icon color. */
@@ -29,12 +23,12 @@ export interface Props {
29
23
  selectIconUp?: string;
30
24
  /** Select Down Icon. */
31
25
  selectIconDown?: string;
32
- /** Select Form Item Name. */
33
- name?: string;
34
- /** Is field disabled. */
35
- disabled?: boolean;
36
26
  /** The selected option's value. */
37
27
  onChange?: (e: React.ChangeEvent<HTMLSelectElement>) => void;
28
+ /** Placeholder text for the select. */
29
+ placeholder?: string;
30
+ /** Validation error message (used by AddressForm). */
31
+ error?: string | false;
38
32
  }
39
33
 
40
34
  export interface ItemProps {
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import React, { useEffect, useState } from 'react';
2
4
  import { Props } from './FullScreenVideoModal.types';
3
5
  import Modal from 'react-modal';
@@ -28,7 +30,7 @@ const FullScreenVideoModal = ({
28
30
  ariaHideApp={ariaHideApp} // Only use in development, adjust for production use
29
31
  data-testid={testID || 'FullScreenVideoModal'}>
30
32
  <div className="h-full relative">
31
- <Button rounded icon="mdi:close" onClick={onCloseCallback} className="absolute top-4 right-4 z-50" />
33
+ <Button rounded icon="mdi:close" onClick={onCloseCallback} ariaLabel="Close video" className="absolute top-4 right-4 z-50" />
32
34
  <Video {...videoProps} />
33
35
  </div>
34
36
  </Modal>
@@ -1,5 +1,7 @@
1
- import { useState } from 'react';
2
- import cx from 'classnames';
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useState } from 'react';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './Header.types';
4
6
  import { useAppContext } from '../../context/App';
5
7
  import Logo from '../Logo';
@@ -29,15 +31,28 @@ const Header = ({
29
31
 
30
32
  const [mobileMenuClass, mobileMenuIcon] = useState('hidden');
31
33
  const [showDropdown, setShowDropdown] = useState(false);
34
+ const isMobileMenuOpen = mobileMenuClass === 'block';
32
35
 
33
36
  const toggleMenuClass = () => {
34
- if (mobileMenuClass === 'hidden') {
35
- mobileMenuIcon('block');
36
- } else {
37
- mobileMenuIcon('hidden');
38
- }
37
+ mobileMenuIcon(isMobileMenuOpen ? 'hidden' : 'block');
39
38
  };
40
39
 
40
+ const closeMobileMenu = useCallback(() => {
41
+ mobileMenuIcon('hidden');
42
+ }, []);
43
+
44
+ // Close dropdown and mobile menu on ESC key
45
+ useEffect(() => {
46
+ const handleEscKey = (event: KeyboardEvent) => {
47
+ if (event.key === 'Escape') {
48
+ if (showDropdown) setShowDropdown(false);
49
+ if (isMobileMenuOpen) closeMobileMenu();
50
+ }
51
+ };
52
+ document.addEventListener('keydown', handleEscKey);
53
+ return () => document.removeEventListener('keydown', handleEscKey);
54
+ }, [showDropdown, isMobileMenuOpen, closeMobileMenu]);
55
+
41
56
  const defaultHeaderStyles = 'relative z-10 w-full m-auto py-4 bg-header-bg dark:bg-header-bg-dark';
42
57
 
43
58
  if (context?.brand?.logoOnly) {
@@ -92,6 +107,9 @@ const Header = ({
92
107
  <Button
93
108
  onClick={() => setShowDropdown(!showDropdown)}
94
109
  icon={!showDropdown ? 'mdi:user' : 'mdi:user-outline'}
110
+ ariaLabel="User menu"
111
+ aria-expanded={showDropdown}
112
+ aria-haspopup="true"
95
113
  />
96
114
  <div
97
115
  onClick={() => setShowDropdown(false)}
@@ -107,7 +125,7 @@ const Header = ({
107
125
  </div>
108
126
  {/* <!-- mobile --> */}
109
127
  <div className="flex md:hidden flex-col justify-center flex-1 items-end">
110
- <Button variant={BTN_VARIANTS.PRIMARY} icon="mdi:menu" onClick={toggleMenuClass} />
128
+ <Button variant={BTN_VARIANTS.PRIMARY} icon="mdi:menu" onClick={toggleMenuClass} ariaLabel="Open menu" aria-expanded={isMobileMenuOpen} />
111
129
  </div>
112
130
  </div>
113
131
  <div className={cx('navbar-menu relative z-50', mobileMenuClass, { hidden: hideMenu })}>
@@ -123,7 +141,7 @@ const Header = ({
123
141
  )}
124
142
  </div>
125
143
  <div>
126
- <Button variant={BTN_VARIANTS.PRIMARY} icon="mdi:close" onClick={toggleMenuClass} />
144
+ <Button variant={BTN_VARIANTS.PRIMARY} icon="mdi:close" onClick={toggleMenuClass} ariaLabel="Close menu" />
127
145
  </div>
128
146
  </div>
129
147
  <div className="flex flex-col flex-1 gap-4">
@@ -1,26 +1,19 @@
1
- import cx from 'classnames';
1
+ import { clsx as cx } from 'clsx';
2
2
  import { HEADING_TAGS, Props } from './Heading.types';
3
3
  import { twMerge } from 'tailwind-merge';
4
4
 
5
- const Heading = ({ children, tag = HEADING_TAGS.H2, transform, align, id, testID, flush, className, singleLine, bold = true }: Props) => {
6
- const fontSize = () => {
7
- switch (tag) {
8
- case 'h1':
9
- return 'text-3xl md:text-4xl lg:text-5xl';
10
- case 'h2':
11
- return 'text-3xl md:text-4xl lg:text-4xl';
12
- case 'h3':
13
- return 'text-xl md:text-2xl lg:text-2xl';
14
- case 'h4':
15
- return 'text-lg md:text-lg lg:text-xl';
16
- case 'h5':
17
- return 'text-base md:text-base lg:text-lg';
18
- default:
19
- return 'text-2xl md:text-2xl lg:text-4xl';
20
- }
21
- };
5
+ const FONT_SIZES: Record<string, string> = {
6
+ h1: 'text-3xl md:text-4xl lg:text-5xl',
7
+ h2: 'text-3xl md:text-4xl lg:text-4xl',
8
+ h3: 'text-xl md:text-2xl lg:text-2xl',
9
+ h4: 'text-lg md:text-lg lg:text-xl',
10
+ h5: 'text-base md:text-base lg:text-lg',
11
+ };
22
12
 
23
- const size = fontSize();
13
+ const DEFAULT_FONT_SIZE = 'text-2xl md:text-2xl lg:text-4xl';
14
+
15
+ const Heading = ({ children, tag = HEADING_TAGS.H2, transform, align, id, testID, flush, className, singleLine, bold = true }: Props) => {
16
+ const size = FONT_SIZES[tag] || DEFAULT_FONT_SIZE;
24
17
 
25
18
  const Tag = tag;
26
19
  const classes = cx(
@@ -23,7 +23,7 @@ export enum HEADING_TRANSFORM {
23
23
 
24
24
  export interface Props {
25
25
  children: string | React.ReactNode;
26
- tag?: HEADING_TAGS;
26
+ tag?: HEADING_TAGS | 'h1' | 'h2' | 'h3' | 'h4' | 'h5';
27
27
  align?: HEADING_ALIGN;
28
28
  transform?: HEADING_TRANSFORM;
29
29
  id?: string;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './Hero.types';
4
4
  import Container from '../Container';
5
5
 
@@ -1,4 +1,4 @@
1
- import cx from 'classnames';
1
+ import { clsx as cx } from 'clsx';
2
2
  import { Props } from './Hr.types';
3
3
 
4
4
  const Hr = ({ size = 'small' }: Props) => (
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import parse from 'html-react-parser';
4
4
  import { Props } from './HtmlContent.types';
5
5
  import { options } from '../../utils/htmlParser';
@@ -1,4 +1,4 @@
1
- import cx from 'classnames';
1
+ import { clsx as cx } from 'clsx';
2
2
  import { Props } from './Image.types';
3
3
  import { createElement } from 'react';
4
4
  import { twMerge } from 'tailwind-merge';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './Label.types';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
 
@@ -1,4 +1,6 @@
1
- import cx from 'classnames';
1
+ "use client";
2
+
3
+ import { clsx as cx } from 'clsx';
2
4
  import { Props } from './Link.types';
3
5
  import React from 'react';
4
6
  import Icon from '../Icon';
@@ -1,4 +1,4 @@
1
- import cx from 'classnames';
1
+ import { clsx as cx } from 'clsx';
2
2
  import { ListTag, Props } from './List.types';
3
3
 
4
4
  const List = ({ variants, children, as = ListTag.UL, id, testID, className }: Props) => {
@@ -1,4 +1,6 @@
1
- import cx from 'classnames';
1
+ "use client";
2
+
3
+ import { clsx as cx } from 'clsx';
2
4
  import { Props } from './ListItem.types';
3
5
  import Icon from '../Icon';
4
6
  import { HEADING_TAGS } from '../Heading/Heading.types';
@@ -4,7 +4,7 @@ const Loader = ({ color, size = 8, thickness = 4 }: Props) => {
4
4
  const sizeClass = size ? `w-${size} h-${size}` : 'w-8 h-8';
5
5
  return (
6
6
  <div data-testid="Loader" className='flex items-center justify-center'>
7
- <div role="status" className='flex items-center'>
7
+ <div role="status" aria-live="polite" className='flex items-center'>
8
8
  <svg
9
9
  className={`animate-spin ${sizeClass} fill-current`}
10
10
  xmlns="http://www.w3.org/2000/svg"
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './Logo.types';
4
4
  import Image from '../Image';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './Menu.types';
4
4
  import Link from '../Link';
5
5
  import Icon from '../Icon';
@@ -1,4 +1,6 @@
1
- import cx from 'classnames';
1
+ "use client";
2
+
3
+ import { clsx as cx } from 'clsx';
2
4
  import { Props } from './Notice.types';
3
5
  import Heading from '../Heading';
4
6
  import { HEADING_TAGS } from '../Heading/Heading.types';
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React, { useMemo, useState } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import ROtpInput from 'react-otp-input';
4
6
  import { Props } from './OTPInput.types';
5
7
  import Icon from '../Icon';
@@ -18,7 +20,7 @@ const OTPInput = ({
18
20
  if (otp && otp.length === 4 && inputCallback !== undefined && typeof inputCallback === 'function') {
19
21
  inputCallback(otp);
20
22
  }
21
- }, [otp]);
23
+ }, [otp, inputCallback]);
22
24
 
23
25
  const sizeRatio = useMemo(() => {
24
26
  switch (size) {
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React, { useRef } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { useInView } from 'framer-motion';
4
6
  import { Props } from './PanelCards.types';
5
7
  import Card from '../Card';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import Container from '../Container';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
  import { Props } from './PanelCustom.types';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './PanelFeature.types';
4
4
  import AnimationSet1 from '../AnimationSet1';
5
5
  import DisplayHeading from '../DisplayHeading';
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React, { useEffect, useMemo, useRef } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './PanelHero.types';
4
6
  import Button from '../Button';
5
7
  import DisplayHeading from '../DisplayHeading';
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import { useEffect, useRef } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import Button from '../Button';
4
6
  import DisplayHeading from '../DisplayHeading';
5
7
  import Container from '../Container';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './PanelProduct.types';
4
4
  import Image from '../Image';
5
5
  import DisplayHeading from '../DisplayHeading';
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import React, { useRef } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Props } from './PanelScroller.types';
4
6
  import Carousel from '../Carousel';
5
7
  import { CardVariant } from '../Card/Card.types';
@@ -1,4 +1,6 @@
1
- import cx from 'classnames';
1
+ "use client";
2
+
3
+ import { clsx as cx } from 'clsx';
2
4
  import { motion } from 'framer-motion';
3
5
  import { Props } from './PanelVideo.types';
4
6
  import Video from '../Video';
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from 'react';
2
4
  import { cva, type VariantProps } from 'class-variance-authority';
3
5
  import { cn } from '../../utils';
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from 'react';
2
4
  import { useState, useMemo } from 'react';
3
5
  import { cn } from '../../utils';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './Radio.types';
4
4
  import Icon from '../Icon';
5
5
 
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { useMemo, useState, useCallback } from 'react';
2
4
  import { Props } from './Schedule.types';
3
5
  import { useLanguage } from '../../hooks';
@@ -31,8 +33,6 @@ const buildDateToClassMap = (scheduleItems: any) => {
31
33
  // Create a simple date key (YYYY-MM-DD) to avoid timezone issues
32
34
  const dateKey = `${normalized.getFullYear()}-${String(normalized.getMonth() + 1).padStart(2, '0')}-${String(normalized.getDate()).padStart(2, '0')}`;
33
35
  dateMap.set(dateKey, { ...classData, weekday });
34
- } else {
35
- console.log(` ✗ No start timestamp for ${weekday}`);
36
36
  }
37
37
  }
38
38
  });
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import React, { useMemo } from 'react';
2
4
  import { format } from 'date-fns';
3
5
  import { Props } from './ScheduleItem.types';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './Screen.types';
4
4
  import Header from '../Header';
5
5
  import Footer from '../Footer';
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react"
2
4
  import useEmblaCarousel, {
3
5
  type UseEmblaCarouselType,
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import React from 'react';
2
4
  import {
3
5
  EmailShareButton,
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import { useState } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { confirmAlert } from 'react-confirm-alert'; // Import
4
6
  import { Props } from './SubscriptionManager.types';
5
7
  import Heading from '../Heading';
@@ -1,5 +1,5 @@
1
1
  import { forwardRef } from 'react';
2
- import cx from 'classnames';
2
+ import { clsx as cx } from 'clsx';
3
3
  import { Props } from './Switch.types';
4
4
 
5
5
  const Switch = forwardRef(function MyInput(
@@ -1,5 +1,7 @@
1
+ "use client";
2
+
1
3
  import { useState } from 'react';
2
- import cx from 'classnames';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { Tab, Tabs as RTabs, TabList, TabPanel } from 'react-tabs';
4
6
  import { Props } from './Tabs.types';
5
7
  import Copy from '../Copy';
@@ -1,5 +1,7 @@
1
- import { forwardRef, useEffect } from 'react';
2
- import cx from 'classnames';
1
+ "use client";
2
+
3
+ import React, { forwardRef, useEffect } from 'react';
4
+ import { clsx as cx } from 'clsx';
3
5
  import { InputType, Props } from './TextInput.types';
4
6
  import Icon from '../Icon';
5
7
 
@@ -14,6 +16,7 @@ const TextInput = forwardRef(function MyInput(
14
16
  disabled = false,
15
17
  name,
16
18
  autoComplete,
19
+ error: _error,
17
20
  ...rest
18
21
  }: Props,
19
22
  ref: any,
@@ -42,7 +45,7 @@ const TextInput = forwardRef(function MyInput(
42
45
  onChange={onChange}
43
46
  rows={4}
44
47
  className={cx('form-item text-area resize-none', className)}
45
- {...rest}
48
+ {...(rest as React.TextareaHTMLAttributes<HTMLTextAreaElement>)}
46
49
  />
47
50
  </div>
48
51
  );