@utilitywarehouse/hearth-react-native 0.14.1 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/CHANGELOG.md +55 -0
  4. package/build/components/BottomSheet/index.d.ts +5 -5
  5. package/build/components/BottomSheet/index.js +4 -4
  6. package/build/components/Checkbox/Checkbox.d.ts +1 -1
  7. package/build/components/Checkbox/Checkbox.js +2 -4
  8. package/build/components/DescriptionList/DescriptionListItem.js +8 -1
  9. package/build/components/HTMLElements/ListItem.d.ts +9 -1
  10. package/build/components/HTMLElements/ListItem.js +1 -2
  11. package/build/components/HTMLElements/OrderedList.d.ts +3 -2
  12. package/build/components/HTMLElements/OrderedList.js +29 -4
  13. package/build/components/HTMLElements/UnorderedList.d.ts +3 -2
  14. package/build/components/HTMLElements/UnorderedList.js +29 -4
  15. package/build/components/Helper/Helper.js +1 -1
  16. package/build/components/Helper/HelperText.js +1 -0
  17. package/build/components/Input/Input.js +2 -2
  18. package/build/components/Modal/Modal.d.ts +1 -1
  19. package/build/components/Modal/Modal.js +49 -4
  20. package/build/components/Modal/Modal.props.d.ts +1 -0
  21. package/build/components/PillGroup/PillGroup.props.d.ts +19 -7
  22. package/package.json +1 -1
  23. package/src/components/BottomSheet/index.ts +7 -5
  24. package/src/components/Checkbox/Checkbox.tsx +7 -2
  25. package/src/components/DescriptionList/DescriptionListItem.tsx +8 -1
  26. package/src/components/HTMLElements/ListItem.tsx +11 -3
  27. package/src/components/HTMLElements/Lists.docs.mdx +64 -16
  28. package/src/components/HTMLElements/OrderedList.stories.tsx +33 -2
  29. package/src/components/HTMLElements/OrderedList.tsx +54 -6
  30. package/src/components/HTMLElements/UnorderedList.stories.tsx +63 -5
  31. package/src/components/HTMLElements/UnorderedList.tsx +50 -6
  32. package/src/components/Helper/Helper.tsx +1 -1
  33. package/src/components/Helper/HelperText.tsx +1 -0
  34. package/src/components/Input/Input.tsx +2 -0
  35. package/src/components/Modal/Modal.props.ts +1 -0
  36. package/src/components/Modal/Modal.tsx +86 -23
  37. package/src/components/PillGroup/PillGroup.props.ts +25 -11
  38. package/src/components/PillGroup/PillGroup.stories.tsx +5 -6
  39. package/src/components/PillGroup/PillGroup.tsx +3 -3
@@ -1,4 +1,4 @@
1
1
 
2
- > @utilitywarehouse/hearth-react-native@0.14.1 build /home/runner/work/hearth/hearth/packages/react-native
2
+ > @utilitywarehouse/hearth-react-native@0.15.1 build /home/runner/work/hearth/hearth/packages/react-native
3
3
  > tsc
4
4
 
@@ -1,5 +1,5 @@
1
1
 
2
- > @utilitywarehouse/hearth-react-native@0.14.1 lint /home/runner/work/hearth/hearth/packages/react-native
2
+ > @utilitywarehouse/hearth-react-native@0.15.1 lint /home/runner/work/hearth/hearth/packages/react-native
3
3
  > TIMING=1 eslint --max-warnings 0
4
4
 
5
5
  Rule | Time (ms) | Relative
package/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # @utilitywarehouse/hearth-react-native
2
2
 
3
+ ## 0.15.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#768](https://github.com/utilitywarehouse/hearth/pull/768) [`570f240`](https://github.com/utilitywarehouse/hearth/commit/570f240a448eae546b893ed3ad69235213ee5fac) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Makes `Modal` footer sticky by default
8
+
9
+ This change updates the `Modal` component to have a sticky footer by default, enhancing user experience by keeping action buttons accessible. The `stickyFooter` prop has been added to allow developers to disable this behavior if needed.
10
+
11
+ ## 0.15.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [#766](https://github.com/utilitywarehouse/hearth/pull/766) [`183155a`](https://github.com/utilitywarehouse/hearth/commit/183155a1aaf7713f0ab8a39ab1e5684ef6190d0c) Thanks [@jordmccord](https://github.com/jordmccord)! - 🌟 [FEATURE] Custom `ListItem` styles for `UL` and `OL` components
16
+
17
+ Added new props to `UL`, `OL`, and `ListItem` components to support custom list markers, including icons, images, and colors. This brings the functionality closer to CSS-like list styling. We also fixed a layout issue where list item text could overflow the container.
18
+
19
+ **Components affected**:
20
+
21
+ - `UL` (UnorderedList)
22
+ - `OL` (OrderedList)
23
+ - `ListItem`
24
+
25
+ **Developer changes**:
26
+
27
+ You can now customise list bullets/markers using the new `listStyle*` props. These can be set on the list container to apply to all items, or overridden on individual list items.
28
+
29
+ ```tsx
30
+ import { UL, LI } from '@utilitywarehouse/hearth-react-native';
31
+ import { TickIcon } from '@utilitywarehouse/hearth-react-native-icons';
32
+
33
+ <UL listStyleColour="feedbackPositiveSurfaceDefault" listStyleIcon={TickIcon}>
34
+ <LI>Success item 1</LI>
35
+ <LI listStyleColour="feedbackDangerSurfaceDefault">Error item override</LI>
36
+ </UL>;
37
+ ```
38
+
39
+ Supported props:
40
+
41
+ - `listStyleImage`: React Element (e.g. `<Image />`)
42
+ - `listStyleIcon`: Icon component
43
+ - `listStyleWidth` / `listStyleHeight`: Dimensions for the marker (default: 20)
44
+ - `listStyleColour`: Color token or value for the marker
45
+
46
+ ### Patch Changes
47
+
48
+ - [#762](https://github.com/utilitywarehouse/hearth/pull/762) [`2d2bbd2`](https://github.com/utilitywarehouse/hearth/commit/2d2bbd2ba109b9acb2e0b220766eb02c0ad5710e) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Fixes Android error when passing boolean or number as a `Checkbox` value
49
+
50
+ - [#764](https://github.com/utilitywarehouse/hearth/pull/764) [`46f115d`](https://github.com/utilitywarehouse/hearth/commit/46f115dd4bd9da824496e8ec19e29276523931a1) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Fixes `HelperText` wrapping issue
51
+
52
+ - [#762](https://github.com/utilitywarehouse/hearth/pull/762) [`2d2bbd2`](https://github.com/utilitywarehouse/hearth/commit/2d2bbd2ba109b9acb2e0b220766eb02c0ad5710e) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Fixes `PillGroup` `onChange` prop types
53
+
54
+ - [#764](https://github.com/utilitywarehouse/hearth/pull/764) [`46f115d`](https://github.com/utilitywarehouse/hearth/commit/46f115dd4bd9da824496e8ec19e29276523931a1) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: `DescriptionListItem` description disappearing when direction is `column`
55
+
56
+ - [#762](https://github.com/utilitywarehouse/hearth/pull/762) [`2d2bbd2`](https://github.com/utilitywarehouse/hearth/commit/2d2bbd2ba109b9acb2e0b220766eb02c0ad5710e) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Input styles are now passed to the correct View
57
+
3
58
  ## 0.14.1
4
59
 
5
60
  ### Patch Changes
@@ -1,7 +1,7 @@
1
+ export { BottomSheetFlashList, BottomSheetFooter, BottomSheetModalProvider, BottomSheetSectionList, BottomSheetVirtualizedList, useBottomSheet, useBottomSheetModal, type BottomSheetBackdropProps, type BottomSheetBackgroundProps, type BottomSheetHandleProps, type BottomSheetModalProps, } from '@gorhom/bottom-sheet';
1
2
  export { default as BottomSheet } from './BottomSheet';
2
- export { default as BottomSheetView } from './BottomSheetView';
3
- export { default as BottomSheetScrollView } from './BottomSheetScrollView';
4
- export { default as BottomSheetModal } from './BottomSheetModal';
5
- export { default as BottomSheetFlatList } from './BottomSheetFlatList';
6
- export { BottomSheetModalProvider, BottomSheetSectionList, BottomSheetVirtualizedList, useBottomSheet, useBottomSheetModal, type BottomSheetBackdropProps, type BottomSheetBackgroundProps, type BottomSheetHandleProps, type BottomSheetModalProps, } from '@gorhom/bottom-sheet';
7
3
  export type { default as BottomSheetProps } from './BottomSheet.props';
4
+ export { default as BottomSheetFlatList } from './BottomSheetFlatList';
5
+ export { default as BottomSheetModal } from './BottomSheetModal';
6
+ export { default as BottomSheetScrollView } from './BottomSheetScrollView';
7
+ export { default as BottomSheetView } from './BottomSheetView';
@@ -1,6 +1,6 @@
1
+ export { BottomSheetFlashList, BottomSheetFooter, BottomSheetModalProvider, BottomSheetSectionList, BottomSheetVirtualizedList, useBottomSheet, useBottomSheetModal, } from '@gorhom/bottom-sheet';
1
2
  export { default as BottomSheet } from './BottomSheet';
2
- export { default as BottomSheetView } from './BottomSheetView';
3
- export { default as BottomSheetScrollView } from './BottomSheetScrollView';
4
- export { default as BottomSheetModal } from './BottomSheetModal';
5
3
  export { default as BottomSheetFlatList } from './BottomSheetFlatList';
6
- export { BottomSheetModalProvider, BottomSheetSectionList, BottomSheetVirtualizedList, useBottomSheet, useBottomSheetModal, } from '@gorhom/bottom-sheet';
4
+ export { default as BottomSheetModal } from './BottomSheetModal';
5
+ export { default as BottomSheetScrollView } from './BottomSheetScrollView';
6
+ export { default as BottomSheetView } from './BottomSheetView';
@@ -10,7 +10,7 @@ declare const CheckboxIcon: import("react").ForwardRefExoticComponent<import("re
10
10
  }>;
11
11
  declare const CheckboxLabel: import("react").ForwardRefExoticComponent<import("react").RefAttributes<import("../Label/Label.props").default> & Omit<import("../Label/Label.props").default, "ref">>;
12
12
  declare const Checkbox: {
13
- ({ children, label, disabled, checked, helperIcon, helperText, invalidText, validText, validationStatus: validation, showValidationIcon, type, image, ...props }: CheckboxProps): import("react/jsx-runtime").JSX.Element;
13
+ ({ children, label, disabled, checked, helperIcon, helperText, invalidText, validText, validationStatus: validation, showValidationIcon, type, image, value, ...props }: CheckboxProps): import("react/jsx-runtime").JSX.Element;
14
14
  displayName: string;
15
15
  };
16
16
  declare const CheckboxTile: {
@@ -25,15 +25,13 @@ CheckboxGroup.displayName = 'CheckboxGroup';
25
25
  CheckboxIndicator.displayName = 'CheckboxIndicator';
26
26
  CheckboxIcon.displayName = 'CheckboxIcon';
27
27
  CheckboxLabel.displayName = 'CheckboxLabel';
28
- const Checkbox = ({ children, label, disabled, checked, helperIcon, helperText, invalidText, validText, validationStatus: validation, showValidationIcon, type = 'default', image, ...props }) => {
28
+ const Checkbox = ({ children, label, disabled, checked, helperIcon, helperText, invalidText, validText, validationStatus: validation, showValidationIcon, type = 'default', image, value, ...props }) => {
29
29
  const { validationStatus: fieldValidationStatus } = useFormFieldContext();
30
30
  const { validationStatus: groupValidationStatus, type: groupType } = useCheckboxGroupContext();
31
31
  const validationStatus = fieldValidationStatus ?? groupValidationStatus ?? validation ?? 'initial';
32
32
  const checkboxType = groupType ?? type;
33
33
  const checkboxChildren = children ? (children) : (_jsxs(_Fragment, { children: [_jsx(CheckboxIndicator, { children: _jsx(CheckboxIcon, {}) }), image ? image : null, _jsxs(CheckboxTextContent, { children: [!!label && _jsx(CheckboxLabel, { children: label }), !!helperText && _jsx(Helper, { disabled: disabled, icon: helperIcon, text: helperText }), validationStatus === 'invalid' && !!invalidText && (_jsx(Helper, { showIcon: showValidationIcon, disabled: disabled, validationStatus: "invalid", text: invalidText })), validationStatus === 'valid' && !!validText && (_jsx(Helper, { disabled: disabled, showIcon: showValidationIcon, validationStatus: "valid", text: validText }))] })] }));
34
- return (
35
- // @ts-expect-error - type
36
- _jsx(CheckboxComponent, { ...props, isDisabled: disabled, isChecked: checked, children: checkboxType === 'tile' ? (_jsx(CheckboxTileRoot, { children: checkboxChildren })) : (checkboxChildren) }));
34
+ return (_jsx(CheckboxComponent, { ...props, value: (value ?? '').toString(), isDisabled: disabled, isChecked: checked, children: checkboxType === 'tile' ? (_jsx(CheckboxTileRoot, { children: checkboxChildren })) : (checkboxChildren) }));
37
35
  };
38
36
  const CheckboxTile = ({ type = 'tile', ...props }) => {
39
37
  return _jsx(Checkbox, { ...props, type: type });
@@ -44,7 +44,14 @@ const styles = StyleSheet.create(theme => ({
44
44
  color: theme.color.text.secondary,
45
45
  },
46
46
  descriptionWrapper: {
47
- flex: 1,
47
+ variants: {
48
+ direction: {
49
+ row: {
50
+ flex: 1,
51
+ },
52
+ column: {},
53
+ },
54
+ },
48
55
  },
49
56
  }));
50
57
  export default DescriptionListItem;
@@ -1,5 +1,13 @@
1
1
  import { ViewProps } from 'react-native';
2
- export interface ListItemProps extends ViewProps {
2
+ import { ColorValue } from '../../types';
3
+ export interface ListStyleProps {
4
+ listStyleImage?: React.ReactElement;
5
+ listStyleIcon?: React.ComponentType<any>;
6
+ listStyleWidth?: number;
7
+ listStyleHeight?: number;
8
+ listStyleColour?: ColorValue;
9
+ }
10
+ export interface ListItemProps extends ViewProps, ListStyleProps {
3
11
  children: ViewProps['children'];
4
12
  }
5
13
  declare const ListItem: {
@@ -7,8 +7,7 @@ const ListItem = ({ children, style, ...rest }) => {
7
7
  ListItem.displayName = 'ListItem';
8
8
  const styles = StyleSheet.create({
9
9
  item: {
10
- flexDirection: 'row',
11
- alignItems: 'flex-start',
10
+ flexShrink: 1,
12
11
  },
13
12
  });
14
13
  export default ListItem;
@@ -1,12 +1,13 @@
1
1
  import { ViewProps, ViewStyle } from 'react-native';
2
2
  import { SpaceValue } from '../../types';
3
- export interface OrderedListProps extends ViewProps {
3
+ import { ListStyleProps } from './ListItem';
4
+ export interface OrderedListProps extends ViewProps, ListStyleProps {
4
5
  children: ViewProps['children'];
5
6
  gap?: SpaceValue;
6
7
  bulletStyle?: ViewStyle;
7
8
  }
8
9
  declare const OrderedList: {
9
- ({ children, gap, style, ...rest }: OrderedListProps): import("react/jsx-runtime").JSX.Element;
10
+ ({ children, gap, style, listStyleImage, listStyleIcon, listStyleWidth, listStyleHeight, listStyleColour, ...rest }: OrderedListProps): import("react/jsx-runtime").JSX.Element;
10
11
  displayName: string;
11
12
  };
12
13
  export default OrderedList;
@@ -1,15 +1,40 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { StyleSheet, View } from 'react-native';
4
- import { useStyleProps } from '../../hooks';
4
+ import { useStyleProps, useTheme } from '../../hooks';
5
+ import { getFlattenedColorValue } from '../../utils';
5
6
  import { BodyText } from '../BodyText';
6
- const OrderedList = ({ children, gap = '100', style, ...rest }) => {
7
+ const OrderedList = ({ children, gap = '100', style, listStyleImage, listStyleIcon, listStyleWidth, listStyleHeight, listStyleColour, ...rest }) => {
7
8
  const { computedStyles } = useStyleProps({ gap });
9
+ const theme = useTheme();
8
10
  let itemNumber = 0;
9
11
  return (_jsx(View, { style: [computedStyles, style], ...rest, children: React.Children.map(children, child => {
10
12
  if (React.isValidElement(child)) {
11
13
  itemNumber++;
12
- return (_jsxs(View, { style: styles.listItemContainer, children: [_jsx(BodyText, { style: styles.number, children: `${itemNumber}.` }), React.cloneElement(child, {})] }));
14
+ const childProps = child.props;
15
+ const image = childProps.listStyleImage ?? listStyleImage;
16
+ const Icon = childProps.listStyleIcon ?? listStyleIcon;
17
+ const width = childProps.listStyleWidth ?? listStyleWidth ?? 20;
18
+ const height = childProps.listStyleHeight ?? listStyleHeight ?? 20;
19
+ const colourRaw = childProps.listStyleColour ?? listStyleColour;
20
+ const colour = colourRaw ? getFlattenedColorValue(colourRaw, theme.color) : undefined;
21
+ let bullet;
22
+ if (image) {
23
+ const imageEl = image;
24
+ bullet = React.cloneElement(imageEl, {
25
+ style: [{ width, height }, imageEl.props.style],
26
+ });
27
+ }
28
+ else if (Icon) {
29
+ bullet = (_jsx(Icon, { width: width, height: height, color: colour ?? theme.color.text.primary }));
30
+ }
31
+ else {
32
+ bullet = (_jsx(BodyText, { style: [styles.number, colour && { color: colour }], children: `${itemNumber}.` }));
33
+ }
34
+ const isCustom = !!(image || Icon);
35
+ return (_jsxs(View, { style: styles.listItemContainer, children: [isCustom ? _jsx(View, { style: { marginRight: 8 }, children: bullet }) : bullet, React.cloneElement(child, {
36
+ style: [childProps.style, { flex: 1 }],
37
+ })] }));
13
38
  }
14
39
  return child;
15
40
  }) }));
@@ -22,7 +47,7 @@ const styles = StyleSheet.create({
22
47
  },
23
48
  number: {
24
49
  marginRight: 8,
25
- lineHeight: undefined, // Allow number to align with first line of text
50
+ lineHeight: undefined,
26
51
  },
27
52
  });
28
53
  export default OrderedList;
@@ -1,12 +1,13 @@
1
1
  import { ViewProps, ViewStyle } from 'react-native';
2
2
  import { SpaceValue } from '../../types';
3
- export interface UnorderedListProps extends ViewProps {
3
+ import { ListStyleProps } from './ListItem';
4
+ export interface UnorderedListProps extends ViewProps, ListStyleProps {
4
5
  children: ViewProps['children'];
5
6
  gap?: SpaceValue;
6
7
  bulletStyle?: ViewStyle;
7
8
  }
8
9
  declare const UnorderedList: {
9
- ({ children, gap, style, ...rest }: UnorderedListProps): import("react/jsx-runtime").JSX.Element;
10
+ ({ children, gap, style, listStyleImage, listStyleIcon, listStyleWidth, listStyleHeight, listStyleColour, ...rest }: UnorderedListProps): import("react/jsx-runtime").JSX.Element;
10
11
  displayName: string;
11
12
  };
12
13
  export default UnorderedList;
@@ -1,13 +1,38 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { StyleSheet, View } from 'react-native';
4
- import { useStyleProps } from '../../hooks';
4
+ import { useStyleProps, useTheme } from '../../hooks';
5
+ import { getFlattenedColorValue } from '../../utils';
5
6
  import { BodyText } from '../BodyText';
6
- const UnorderedList = ({ children, gap = '100', style, ...rest }) => {
7
+ const UnorderedList = ({ children, gap = '100', style, listStyleImage, listStyleIcon, listStyleWidth, listStyleHeight, listStyleColour, ...rest }) => {
7
8
  const { computedStyles } = useStyleProps({ gap });
9
+ const theme = useTheme();
8
10
  return (_jsx(View, { style: [computedStyles, style], ...rest, children: React.Children.map(children, child => {
9
11
  if (React.isValidElement(child)) {
10
- return (_jsxs(View, { style: styles.listItemContainer, children: [_jsx(BodyText, { style: styles.bullet, children: "\u2022" }), React.cloneElement(child, {})] }));
12
+ const childProps = child.props;
13
+ const image = childProps.listStyleImage ?? listStyleImage;
14
+ const Icon = childProps.listStyleIcon ?? listStyleIcon;
15
+ const width = childProps.listStyleWidth ?? listStyleWidth ?? 20;
16
+ const height = childProps.listStyleHeight ?? listStyleHeight ?? 20;
17
+ const colourRaw = childProps.listStyleColour ?? listStyleColour;
18
+ const colour = colourRaw ? getFlattenedColorValue(colourRaw, theme.color) : undefined;
19
+ let bullet;
20
+ if (image) {
21
+ const imageEl = image;
22
+ bullet = React.cloneElement(imageEl, {
23
+ style: [{ width, height }, imageEl.props.style],
24
+ });
25
+ }
26
+ else if (Icon) {
27
+ bullet = (_jsx(Icon, { width: width, height: height, color: colour ?? theme.color.text.primary }));
28
+ }
29
+ else {
30
+ bullet = _jsx(BodyText, { style: [styles.bullet, colour && { color: colour }], children: "\u2022" });
31
+ }
32
+ const isCustom = !!(image || Icon);
33
+ return (_jsxs(View, { style: styles.listItemContainer, children: [isCustom ? _jsx(View, { style: { marginRight: 8 }, children: bullet }) : bullet, React.cloneElement(child, {
34
+ style: [childProps.style, { flex: 1 }],
35
+ })] }));
11
36
  }
12
37
  return child;
13
38
  }) }));
@@ -20,7 +45,7 @@ const styles = StyleSheet.create({
20
45
  },
21
46
  bullet: {
22
47
  marginRight: 8,
23
- lineHeight: undefined, // Allow bullet to align with first line of text
48
+ lineHeight: undefined,
24
49
  },
25
50
  });
26
51
  export default UnorderedList;
@@ -23,7 +23,7 @@ const styles = StyleSheet.create(theme => ({
23
23
  container: {
24
24
  flexDirection: 'row',
25
25
  gap: theme.components.formField.helper.gap,
26
- alignItems: 'center',
26
+ alignItems: 'flex-start',
27
27
  variants: {
28
28
  disabled: {
29
29
  true: {
@@ -11,6 +11,7 @@ HelperText.displayName = 'HelperText';
11
11
  const styles = StyleSheet.create(theme => ({
12
12
  text: {
13
13
  color: theme.color.text.secondary,
14
+ flexShrink: 1,
14
15
  variants: {
15
16
  validationStatus: {
16
17
  valid: {
@@ -19,7 +19,7 @@ export const InputComponent = createInput({
19
19
  export const InputSlot = InputComponent.Slot;
20
20
  export const InputField = InputComponent.Input;
21
21
  export const InputIcon = InputComponent.Icon;
22
- const Input = forwardRef(({ validationStatus = 'initial', children, disabled, focused, readonly, leadingIcon, trailingIcon, type, showPasswordToggle = true, onClear, format, loading, clearable = false, required, inBottomSheet = false, ...props }, ref) => {
22
+ const Input = forwardRef(({ validationStatus = 'initial', children, disabled, focused, readonly, leadingIcon, trailingIcon, type, showPasswordToggle = true, onClear, format, loading, clearable = false, required, inBottomSheet = false, style, ...props }, ref) => {
23
23
  const formFieldContext = useFormFieldContext();
24
24
  const { disabled: formFieldDisabled } = formFieldContext;
25
25
  const validationStatusFromContext = formFieldContext?.validationStatus ?? validationStatus;
@@ -46,7 +46,7 @@ const Input = forwardRef(({ validationStatus = 'initial', children, disabled, fo
46
46
  }
47
47
  return undefined;
48
48
  })();
49
- return (_jsx(InputComponent, { ...(children ? props : {}), validationStatus: validationStatusFromContext, isInvalid: validationStatusFromContext === 'invalid', isReadOnly: readonly, isDisabled: formFieldDisabled ?? disabled, isFocused: focused, type: type, isRequired: isRequired, children: children ? (_jsx(_Fragment, { children: children })) : (_jsxs(_Fragment, { children: [!!leadingIconComponent && (_jsx(InputSlot, { children: _jsx(InputIcon, { as: leadingIconComponent }) })), _jsx(InputField
49
+ return (_jsx(InputComponent, { ...(children ? props : {}), validationStatus: validationStatusFromContext, isInvalid: validationStatusFromContext === 'invalid', isReadOnly: readonly, isDisabled: formFieldDisabled ?? disabled, isFocused: focused, type: type, isRequired: isRequired, style: style, children: children ? (_jsx(_Fragment, { children: children })) : (_jsxs(_Fragment, { children: [!!leadingIconComponent && (_jsx(InputSlot, { children: _jsx(InputIcon, { as: leadingIconComponent }) })), _jsx(InputField
50
50
  // @ts-expect-error - ref forwarding issue
51
51
  , {
52
52
  // @ts-expect-error - ref forwarding issue
@@ -3,5 +3,5 @@ import ModalProps from './Modal.props';
3
3
  type Modal<T = any> = BottomSheetModalMethods<T> & {
4
4
  triggerCloseAnimation?: () => void;
5
5
  };
6
- declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, fullscreen, image, primaryButtonProps, secondaryButtonProps, closeButtonProps, inNavModal, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
6
+ declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, fullscreen, image, primaryButtonProps, secondaryButtonProps, closeButtonProps, inNavModal, stickyFooter, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
7
7
  export default Modal;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { BottomSheetFooter, } from '@gorhom/bottom-sheet';
2
3
  import { CloseMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
3
4
  import { useCallback, useEffect, useImperativeHandle, useRef } from 'react';
4
5
  import { AccessibilityInfo, Platform, View, findNodeHandle } from 'react-native';
@@ -12,7 +13,7 @@ import { Button } from '../Button';
12
13
  import { Heading } from '../Heading';
13
14
  import { Spinner } from '../Spinner';
14
15
  import { UnstyledIconButton } from '../UnstyledIconButton';
15
- const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, fullscreen = false, image, primaryButtonProps, secondaryButtonProps, closeButtonProps, inNavModal = false, ...props }) => {
16
+ const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, fullscreen = false, image, primaryButtonProps, secondaryButtonProps, closeButtonProps, inNavModal = false, stickyFooter = true, ...props }) => {
16
17
  const bottomSheetModalRef = useRef(null);
17
18
  const viewRef = useRef(null);
18
19
  const scrollViewRef = useRef(null);
@@ -97,9 +98,24 @@ const Modal = ({ ref, children, heading, description, showCloseButton = true, pr
97
98
  bottomSheetModalRef.current?.dismiss();
98
99
  }
99
100
  };
100
- styles.useVariants({ loading });
101
- const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Loading' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: "Loading..." })] })) : (_jsxs(View, { style: styles.container, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, description && !image ? _jsx(BodyText, { accessible: true, children: description }) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, description ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : null] })] })) : null, children, _jsxs(View, { style: styles.footer, children: [onPressPrimaryButton && primaryButtonText ? (_jsx(Button, { onPress: handlePrimaryButtonPress, text: primaryButtonText, ...primaryButtonProps, variant: primaryButtonProps?.variant ?? 'solid', colorScheme: primaryButtonProps?.colorScheme ?? 'highlight' })) : null, onPressSecondaryButton && secondaryButtonText ? (_jsx(Button, { onPress: handleSecondaryButtonPress, text: secondaryButtonText, ...secondaryButtonProps, variant: secondaryButtonProps?.variant ?? 'outline', colorScheme: secondaryButtonProps?.colorScheme ?? 'functional' })) : null] })] })) }));
102
- return inNavModal ? (_jsxs(View, { style: { flex: 1, backgroundColor: theme.color.background.primary }, children: [Platform.OS === 'android' ? (_jsx(Animated.View, { style: [styles.androidContainer, animatedBackgroundStyle], children: _jsx(Animated.View, { style: [styles.pretendContent, animatedPretendContentStyle] }) })) : null, _jsx(Animated.View, { style: [styles.inNavModalContainer, Platform.OS === 'android' && animatedInNavModalStyle], children: _jsx(View, { style: styles.inNavModalContent, children: content }) })] })) : (_jsxs(BottomSheetModal, { ref: bottomSheetModalRef, enableDynamicSizing: true, snapPoints: image || fullscreen ? ['90%'] : props.snapPoints, showHandle: typeof loading !== 'undefined' && loading ? false : props.showHandle, accessible: false, style: styles.modal, ...props, onChange: handleChange, children: [loading ? _jsx(View, { style: styles.loadingTop }) : null, _jsx(BottomSheetScrollView, { contentContainerStyle: styles.container, ref: scrollViewRef, children: content })] }));
101
+ const noButtons = !onPressPrimaryButton && !onPressSecondaryButton;
102
+ styles.useVariants({
103
+ loading,
104
+ bothButtons: !!(onPressPrimaryButton && onPressSecondaryButton),
105
+ noButtons,
106
+ stickyFooter,
107
+ });
108
+ const footer = (_jsxs(View, { style: styles.footer, children: [onPressPrimaryButton && primaryButtonText ? (_jsx(Button, { onPress: handlePrimaryButtonPress, text: primaryButtonText, ...primaryButtonProps, variant: primaryButtonProps?.variant ?? 'solid', colorScheme: primaryButtonProps?.colorScheme ?? 'highlight' })) : null, onPressSecondaryButton && secondaryButtonText ? (_jsx(Button, { onPress: handleSecondaryButtonPress, text: secondaryButtonText, ...secondaryButtonProps, variant: secondaryButtonProps?.variant ?? 'outline', colorScheme: secondaryButtonProps?.colorScheme ?? 'functional' })) : null] }));
109
+ const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Loading' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: "Loading..." })] })) : (_jsxs(View, { style: styles.container, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, description && !image ? _jsx(BodyText, { accessible: true, children: description }) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, description ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : null] })] })) : null, children, !stickyFooter && !noButtons ? footer : null] })) }));
110
+ const renderFooter = useCallback((props) => (_jsx(BottomSheetFooter, { ...props, children: _jsx(View, { style: styles.footerWrap, children: footer }) })), [
111
+ onPressPrimaryButton,
112
+ primaryButtonText,
113
+ onPressSecondaryButton,
114
+ secondaryButtonText,
115
+ primaryButtonProps,
116
+ secondaryButtonProps,
117
+ ]);
118
+ return inNavModal ? (_jsxs(View, { style: { flex: 1, backgroundColor: theme.color.background.primary }, children: [Platform.OS === 'android' ? (_jsx(Animated.View, { style: [styles.androidContainer, animatedBackgroundStyle], children: _jsx(Animated.View, { style: [styles.pretendContent, animatedPretendContentStyle] }) })) : null, _jsx(Animated.View, { style: [styles.inNavModalContainer, Platform.OS === 'android' && animatedInNavModalStyle], children: _jsx(View, { style: styles.inNavModalContent, children: content }) })] })) : (_jsxs(BottomSheetModal, { ref: bottomSheetModalRef, enableDynamicSizing: true, snapPoints: image || fullscreen ? ['90%'] : props.snapPoints, showHandle: typeof loading !== 'undefined' && loading ? false : props.showHandle, accessible: false, style: styles.modal, footerComponent: stickyFooter && !noButtons ? renderFooter : undefined, ...props, onChange: handleChange, children: [loading ? _jsx(View, { style: styles.loadingTop }) : null, _jsx(BottomSheetScrollView, { contentContainerStyle: styles.scrollView, ref: scrollViewRef, children: content })] }));
103
119
  };
104
120
  const styles = StyleSheet.create((theme, rt) => ({
105
121
  modal: {
@@ -116,6 +132,30 @@ const styles = StyleSheet.create((theme, rt) => ({
116
132
  },
117
133
  },
118
134
  },
135
+ scrollView: {
136
+ flex: 1,
137
+ variants: {
138
+ bothButtons: {
139
+ true: {
140
+ paddingBottom: 166 + rt.insets.bottom - theme.components.modal.padding,
141
+ },
142
+ false: {
143
+ paddingBottom: 102 + rt.insets.bottom - theme.components.modal.padding,
144
+ },
145
+ },
146
+ noButtons: {
147
+ true: {
148
+ paddingBottom: rt.insets.bottom + theme.components.modal.padding,
149
+ },
150
+ },
151
+ stickyFooter: {
152
+ true: {},
153
+ false: {
154
+ paddingBottom: rt.insets.bottom + theme.components.modal.padding,
155
+ },
156
+ },
157
+ },
158
+ },
119
159
  header: {
120
160
  flexDirection: 'row',
121
161
  gap: theme.components.modal.gap,
@@ -149,6 +189,11 @@ const styles = StyleSheet.create((theme, rt) => ({
149
189
  footer: {
150
190
  gap: theme.components.modal.action.gap,
151
191
  },
192
+ footerWrap: {
193
+ backgroundColor: theme.color.surface.neutral.strong,
194
+ paddingHorizontal: theme.components.bottomSheet.padding,
195
+ paddingBottom: theme.components.bottomSheet.padding + rt.insets.bottom,
196
+ },
152
197
  inNavModalContainer: {
153
198
  flex: 1,
154
199
  ...(Platform.OS === 'ios' ? { backgroundColor: theme.components.overlay.backgroundColor } : {}),
@@ -11,6 +11,7 @@ interface ModalProps extends Omit<BottomSheetProps, 'children'> {
11
11
  description?: string;
12
12
  inNavModal?: boolean;
13
13
  fullscreen?: boolean;
14
+ stickyFooter?: boolean;
14
15
  children?: ViewProps['children'];
15
16
  onPressPrimaryButton?: () => void;
16
17
  primaryButtonText?: string;
@@ -1,15 +1,27 @@
1
1
  import React from 'react';
2
2
  import { ScrollViewProps, ViewStyle } from 'react-native';
3
- export interface PillGroupProps extends Omit<ScrollViewProps, 'horizontal' | 'contentContainerStyle' | 'showsHorizontalScrollIndicator'> {
4
- /** Controlled selected value(s) */
5
- value: string | string[];
6
- /** Multi-select mode. Default = false */
7
- multiple?: boolean;
3
+ export interface PillGroupBaseProps extends Omit<ScrollViewProps, 'horizontal' | 'contentContainerStyle' | 'showsHorizontalScrollIndicator'> {
8
4
  /** Allow pills to wrap lines. Default = true */
9
5
  wrap?: boolean;
10
- /** Handle selection changes */
11
- onChange?: (value: string | string[]) => void;
12
6
  /** Children must be <Pill> elements */
13
7
  children: React.ReactNode;
14
8
  style?: ViewStyle | ViewStyle[];
15
9
  }
10
+ interface SinglePillGroupProps extends PillGroupBaseProps {
11
+ /** Multi-select mode. Default = false */
12
+ multiple?: false;
13
+ /** Controlled selected value */
14
+ value: string;
15
+ /** Handle selection changes */
16
+ onChange?: (value: string) => void;
17
+ }
18
+ interface MultiPillGroupProps extends PillGroupBaseProps {
19
+ /** Multi-select mode. Default = false */
20
+ multiple: true;
21
+ /** Controlled selected value(s) */
22
+ value: string[];
23
+ /** Handle selection changes */
24
+ onChange?: (value: string[]) => void;
25
+ }
26
+ export type PillGroupProps = SinglePillGroupProps | MultiPillGroupProps;
27
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utilitywarehouse/hearth-react-native",
3
- "version": "0.14.1",
3
+ "version": "0.15.1",
4
4
  "description": "Utility Warehouse React Native UI library",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -1,9 +1,6 @@
1
- export { default as BottomSheet } from './BottomSheet';
2
- export { default as BottomSheetView } from './BottomSheetView';
3
- export { default as BottomSheetScrollView } from './BottomSheetScrollView';
4
- export { default as BottomSheetModal } from './BottomSheetModal';
5
- export { default as BottomSheetFlatList } from './BottomSheetFlatList';
6
1
  export {
2
+ BottomSheetFlashList,
3
+ BottomSheetFooter,
7
4
  BottomSheetModalProvider,
8
5
  BottomSheetSectionList,
9
6
  BottomSheetVirtualizedList,
@@ -14,4 +11,9 @@ export {
14
11
  type BottomSheetHandleProps,
15
12
  type BottomSheetModalProps,
16
13
  } from '@gorhom/bottom-sheet';
14
+ export { default as BottomSheet } from './BottomSheet';
17
15
  export type { default as BottomSheetProps } from './BottomSheet.props';
16
+ export { default as BottomSheetFlatList } from './BottomSheetFlatList';
17
+ export { default as BottomSheetModal } from './BottomSheetModal';
18
+ export { default as BottomSheetScrollView } from './BottomSheetScrollView';
19
+ export { default as BottomSheetView } from './BottomSheetView';
@@ -42,6 +42,7 @@ const Checkbox = ({
42
42
  showValidationIcon,
43
43
  type = 'default',
44
44
  image,
45
+ value,
45
46
  ...props
46
47
  }: CheckboxProps) => {
47
48
  const { validationStatus: fieldValidationStatus } = useFormFieldContext();
@@ -80,8 +81,12 @@ const Checkbox = ({
80
81
  </>
81
82
  );
82
83
  return (
83
- // @ts-expect-error - type
84
- <CheckboxComponent {...props} isDisabled={disabled} isChecked={checked}>
84
+ <CheckboxComponent
85
+ {...props}
86
+ value={(value ?? '').toString()}
87
+ isDisabled={disabled}
88
+ isChecked={checked}
89
+ >
85
90
  {checkboxType === 'tile' ? (
86
91
  <CheckboxTileRoot>{checkboxChildren}</CheckboxTileRoot>
87
92
  ) : (
@@ -86,7 +86,14 @@ const styles = StyleSheet.create(theme => ({
86
86
  color: theme.color.text.secondary,
87
87
  },
88
88
  descriptionWrapper: {
89
- flex: 1,
89
+ variants: {
90
+ direction: {
91
+ row: {
92
+ flex: 1,
93
+ },
94
+ column: {},
95
+ },
96
+ },
90
97
  },
91
98
  }));
92
99
 
@@ -1,7 +1,16 @@
1
1
  import { StyleSheet, View, ViewProps } from 'react-native';
2
+ import { ColorValue } from '../../types';
2
3
  import { BodyText } from '../BodyText';
3
4
 
4
- export interface ListItemProps extends ViewProps {
5
+ export interface ListStyleProps {
6
+ listStyleImage?: React.ReactElement;
7
+ listStyleIcon?: React.ComponentType<any>;
8
+ listStyleWidth?: number;
9
+ listStyleHeight?: number;
10
+ listStyleColour?: ColorValue;
11
+ }
12
+
13
+ export interface ListItemProps extends ViewProps, ListStyleProps {
5
14
  children: ViewProps['children'];
6
15
  }
7
16
 
@@ -17,8 +26,7 @@ ListItem.displayName = 'ListItem';
17
26
 
18
27
  const styles = StyleSheet.create({
19
28
  item: {
20
- flexDirection: 'row',
21
- alignItems: 'flex-start',
29
+ flexShrink: 1,
22
30
  },
23
31
  });
24
32