@utilitywarehouse/hearth-react-native 0.9.0 → 0.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 (106) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/CHANGELOG.md +16 -0
  4. package/build/components/Avatar/Avatar.d.ts +6 -0
  5. package/build/components/Avatar/Avatar.js +80 -0
  6. package/build/components/Avatar/Avatar.props.d.ts +28 -0
  7. package/build/components/Avatar/Avatar.props.js +1 -0
  8. package/build/components/Avatar/index.d.ts +2 -0
  9. package/build/components/Avatar/index.js +1 -0
  10. package/build/components/DateInput/DateInput.d.ts +6 -0
  11. package/build/components/DateInput/DateInput.js +19 -0
  12. package/build/components/DateInput/DateInput.props.d.ts +79 -0
  13. package/build/components/DateInput/DateInput.props.js +1 -0
  14. package/build/components/DateInput/DateInputSegment.d.ts +20 -0
  15. package/build/components/DateInput/DateInputSegment.js +31 -0
  16. package/build/components/DateInput/index.d.ts +2 -0
  17. package/build/components/DateInput/index.js +1 -0
  18. package/build/components/PillGroup/Pill.d.ts +16 -0
  19. package/build/components/PillGroup/Pill.js +94 -0
  20. package/build/components/PillGroup/Pill.props.d.ts +10 -0
  21. package/build/components/PillGroup/Pill.props.js +1 -0
  22. package/build/components/PillGroup/PillGroup.context.d.ts +6 -0
  23. package/build/components/PillGroup/PillGroup.context.js +5 -0
  24. package/build/components/PillGroup/PillGroup.d.ts +5 -0
  25. package/build/components/PillGroup/PillGroup.js +34 -0
  26. package/build/components/PillGroup/PillGroup.props.d.ts +15 -0
  27. package/build/components/PillGroup/PillGroup.props.js +1 -0
  28. package/build/components/PillGroup/index.d.ts +4 -0
  29. package/build/components/PillGroup/index.js +2 -0
  30. package/build/components/Select/Select.js +2 -1
  31. package/build/components/Toast/Toast.context.d.ts +9 -0
  32. package/build/components/Toast/Toast.context.js +90 -0
  33. package/build/components/Toast/Toast.props.d.ts +29 -0
  34. package/build/components/Toast/Toast.props.js +1 -0
  35. package/build/components/Toast/ToastItem.d.ts +10 -0
  36. package/build/components/Toast/ToastItem.js +129 -0
  37. package/build/components/Toast/index.d.ts +3 -0
  38. package/build/components/Toast/index.js +2 -0
  39. package/build/components/index.d.ts +4 -0
  40. package/build/components/index.js +4 -0
  41. package/build/tokens/components/dark/checkbox.d.ts +3 -0
  42. package/build/tokens/components/dark/checkbox.js +3 -0
  43. package/build/tokens/components/dark/input.d.ts +6 -0
  44. package/build/tokens/components/dark/input.js +6 -0
  45. package/build/tokens/components/dark/radio.d.ts +3 -0
  46. package/build/tokens/components/dark/radio.js +3 -0
  47. package/build/tokens/components/dark/table.d.ts +2 -0
  48. package/build/tokens/components/dark/table.js +2 -0
  49. package/build/tokens/components/dark/toast.d.ts +6 -2
  50. package/build/tokens/components/dark/toast.js +6 -2
  51. package/build/tokens/components/light/checkbox.d.ts +3 -0
  52. package/build/tokens/components/light/checkbox.js +3 -0
  53. package/build/tokens/components/light/input.d.ts +6 -0
  54. package/build/tokens/components/light/input.js +6 -0
  55. package/build/tokens/components/light/radio.d.ts +3 -0
  56. package/build/tokens/components/light/radio.js +3 -0
  57. package/build/tokens/components/light/table.d.ts +2 -0
  58. package/build/tokens/components/light/table.js +2 -0
  59. package/build/tokens/components/light/toast.d.ts +6 -2
  60. package/build/tokens/components/light/toast.js +6 -2
  61. package/build/utils/getInitials.d.ts +1 -0
  62. package/build/utils/getInitials.js +8 -0
  63. package/build/utils/index.d.ts +1 -0
  64. package/build/utils/index.js +1 -0
  65. package/docs/assets/toast-ios.MP4 +0 -0
  66. package/docs/components/AllComponents.web.tsx +43 -0
  67. package/package.json +3 -3
  68. package/src/components/Avatar/Avatar.docs.mdx +105 -0
  69. package/src/components/Avatar/Avatar.props.ts +31 -0
  70. package/src/components/Avatar/Avatar.stories.tsx +77 -0
  71. package/src/components/Avatar/Avatar.tsx +136 -0
  72. package/src/components/Avatar/index.ts +2 -0
  73. package/src/components/DateInput/DateInput.docs.mdx +163 -0
  74. package/src/components/DateInput/DateInput.props.ts +80 -0
  75. package/src/components/DateInput/DateInput.stories.tsx +269 -0
  76. package/src/components/DateInput/DateInput.tsx +117 -0
  77. package/src/components/DateInput/DateInputSegment.tsx +83 -0
  78. package/src/components/DateInput/index.ts +2 -0
  79. package/src/components/PillGroup/Pill.props.ts +13 -0
  80. package/src/components/PillGroup/Pill.tsx +120 -0
  81. package/src/components/PillGroup/PillGroup.context.tsx +12 -0
  82. package/src/components/PillGroup/PillGroup.docs.mdx +96 -0
  83. package/src/components/PillGroup/PillGroup.props.ts +22 -0
  84. package/src/components/PillGroup/PillGroup.stories.tsx +159 -0
  85. package/src/components/PillGroup/PillGroup.tsx +66 -0
  86. package/src/components/PillGroup/index.ts +4 -0
  87. package/src/components/Select/Select.tsx +2 -0
  88. package/src/components/Toast/Toast.context.tsx +118 -0
  89. package/src/components/Toast/Toast.docs.mdx +164 -0
  90. package/src/components/Toast/Toast.props.ts +33 -0
  91. package/src/components/Toast/Toast.stories.tsx +356 -0
  92. package/src/components/Toast/ToastItem.tsx +200 -0
  93. package/src/components/Toast/index.ts +3 -0
  94. package/src/components/index.ts +4 -0
  95. package/src/tokens/components/dark/checkbox.ts +3 -0
  96. package/src/tokens/components/dark/input.ts +6 -0
  97. package/src/tokens/components/dark/radio.ts +3 -0
  98. package/src/tokens/components/dark/table.ts +2 -0
  99. package/src/tokens/components/dark/toast.ts +6 -2
  100. package/src/tokens/components/light/checkbox.ts +3 -0
  101. package/src/tokens/components/light/input.ts +6 -0
  102. package/src/tokens/components/light/radio.ts +3 -0
  103. package/src/tokens/components/light/table.ts +2 -0
  104. package/src/tokens/components/light/toast.ts +6 -2
  105. package/src/utils/getInitials.ts +7 -0
  106. package/src/utils/index.ts +1 -0
@@ -1,4 +1,4 @@
1
1
 
2
- > @utilitywarehouse/hearth-react-native@0.9.0 build /home/runner/work/hearth/hearth/packages/react-native
2
+ > @utilitywarehouse/hearth-react-native@0.11.0 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.9.0 lint /home/runner/work/hearth/hearth/packages/react-native
2
+ > @utilitywarehouse/hearth-react-native@0.11.0 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,21 @@
1
1
  # @utilitywarehouse/hearth-react-native
2
2
 
3
+ ## 0.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#674](https://github.com/utilitywarehouse/hearth/pull/674) [`c617257`](https://github.com/utilitywarehouse/hearth/commit/c617257974b11c39d706d6fd46712a284ff5fe10) Thanks [@jordmccord](https://github.com/jordmccord)! - Adds `DateInput` component
8
+
9
+ - [#663](https://github.com/utilitywarehouse/hearth/pull/663) [`8889a07`](https://github.com/utilitywarehouse/hearth/commit/8889a07e347e9289928e679cee495d7656a4e9aa) Thanks [@Utakato](https://github.com/Utakato)! - Add Avatar component
10
+
11
+ ## 0.10.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [#659](https://github.com/utilitywarehouse/hearth/pull/659) [`99afbed`](https://github.com/utilitywarehouse/hearth/commit/99afbed95df550d5d5a8bb6e04f7640077bf4883) Thanks [@MichalCiesliczka](https://github.com/MichalCiesliczka)! - Adds `Pill` and `PillGroup` components
16
+
17
+ - [#671](https://github.com/utilitywarehouse/hearth/pull/671) [`706448d`](https://github.com/utilitywarehouse/hearth/commit/706448d91f354cb96a71a5f3acc9d17aaa767078) Thanks [@jordmccord](https://github.com/jordmccord)! - Adds `Toast` component
18
+
3
19
  ## 0.9.0
4
20
 
5
21
  ### Minor Changes
@@ -0,0 +1,6 @@
1
+ import AvatarProps from './Avatar.props';
2
+ declare const Avatar: {
3
+ ({ src, name, size, delayMs, onLoadingStatusChange, style, ...props }: AvatarProps): import("react/jsx-runtime").JSX.Element;
4
+ displayName: string;
5
+ };
6
+ export default Avatar;
@@ -0,0 +1,80 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { UserMediumIcon, UserSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
3
+ import { useEffect, useState } from 'react';
4
+ import { Image, View } from 'react-native';
5
+ import { StyleSheet } from 'react-native-unistyles';
6
+ import { useTheme } from '../../hooks';
7
+ import { getInitials } from '../../utils';
8
+ import BodyText from '../BodyText/BodyText';
9
+ const Avatar = ({ src, name, size = 'md', delayMs = 0, onLoadingStatusChange, style, ...props }) => {
10
+ const [status, setStatus] = useState('idle');
11
+ const [isDelayed, setIsDelayed] = useState(delayMs > 0);
12
+ const { components } = useTheme();
13
+ useEffect(() => {
14
+ if (!src) {
15
+ setStatus('idle');
16
+ return;
17
+ }
18
+ setStatus('loading');
19
+ }, [src]);
20
+ useEffect(() => {
21
+ onLoadingStatusChange?.(status);
22
+ }, [status, onLoadingStatusChange]);
23
+ useEffect(() => {
24
+ if (delayMs <= 0) {
25
+ setIsDelayed(false);
26
+ return;
27
+ }
28
+ const timerId = setTimeout(() => {
29
+ setIsDelayed(false);
30
+ }, delayMs);
31
+ return () => clearTimeout(timerId);
32
+ }, [delayMs]);
33
+ styles.useVariants({ size });
34
+ const initials = getInitials(name);
35
+ const handleLoad = () => setStatus('loaded');
36
+ const handleError = () => setStatus('error');
37
+ const showImage = src && status === 'loaded';
38
+ const showFallback = !showImage && !isDelayed;
39
+ const textSize = size === 'sm' ? 'md' : 'lg';
40
+ const FallbackIcon = size === 'sm' ? UserSmallIcon : UserMediumIcon;
41
+ return (_jsxs(View, { ...props, style: [styles.container, style], accessibilityRole: showImage && name ? 'image' : undefined, accessibilityLabel: showImage ? name : undefined, children: [src && (_jsx(Image, { source: src, style: styles.image, onLoad: handleLoad, onError: handleError, accessibilityElementsHidden: true })), showFallback && (_jsx(View, { style: styles.fallback, children: name ? (_jsx(BodyText, { size: textSize, weight: "semibold", textTransform: "uppercase", style: styles.text, children: initials })) : (_jsx(FallbackIcon, {})) }))] }));
42
+ };
43
+ Avatar.displayName = 'Avatar';
44
+ const styles = StyleSheet.create(theme => ({
45
+ container: {
46
+ justifyContent: 'center',
47
+ alignItems: 'center',
48
+ overflow: 'hidden',
49
+ backgroundColor: theme.color.surface.pig.subtle,
50
+ variants: {
51
+ size: {
52
+ sm: {
53
+ width: theme.components.avatar.sm.width,
54
+ height: theme.components.avatar.sm.height,
55
+ borderRadius: theme.components.avatar.sm.borderRadius,
56
+ },
57
+ md: {
58
+ width: theme.components.avatar.md.width,
59
+ height: theme.components.avatar.md.height,
60
+ borderRadius: theme.components.avatar.md.borderRadius,
61
+ },
62
+ },
63
+ },
64
+ },
65
+ image: {
66
+ width: '100%',
67
+ height: '100%',
68
+ position: 'absolute',
69
+ },
70
+ fallback: {
71
+ width: '100%',
72
+ height: '100%',
73
+ justifyContent: 'center',
74
+ alignItems: 'center',
75
+ },
76
+ text: {
77
+ color: theme.color.text.primary,
78
+ },
79
+ }));
80
+ export default Avatar;
@@ -0,0 +1,28 @@
1
+ import type { ImageSourcePropType, ViewProps } from 'react-native';
2
+ export type AvatarLoadingStatus = 'idle' | 'loading' | 'loaded' | 'error';
3
+ interface AvatarProps extends ViewProps {
4
+ /**
5
+ * The image source to display.
6
+ */
7
+ src?: ImageSourcePropType;
8
+ /**
9
+ * The name associated with the avatar, used for creating initials and accessibility label.
10
+ */
11
+ name?: string;
12
+ /**
13
+ * Sets the avatar size.
14
+ * @default md
15
+ */
16
+ size?: 'sm' | 'md';
17
+ /**
18
+ * Delay in milliseconds before the image is rendered.
19
+ * Useful to prevent flickering when the image loads very quickly.
20
+ * @default 0
21
+ */
22
+ delayMs?: number;
23
+ /**
24
+ * Callback fired when the loading status of the image changes.
25
+ */
26
+ onLoadingStatusChange?: (status: AvatarLoadingStatus) => void;
27
+ }
28
+ export default AvatarProps;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export { default as Avatar } from './Avatar';
2
+ export type { default as AvatarProps, AvatarLoadingStatus } from './Avatar.props';
@@ -0,0 +1 @@
1
+ export { default as Avatar } from './Avatar';
@@ -0,0 +1,6 @@
1
+ import type { DateInputProps } from './DateInput.props';
2
+ declare const DateInput: {
3
+ ({ label, helperText, helperIcon, validationStatus, validText, invalidText, disabled, readonly, required, hideDay, hideMonth, hideYear, dayPlaceholder, monthPlaceholder, yearPlaceholder, dayValue, monthValue, yearValue, onDayChange, onMonthChange, onYearChange, onDayFocus, onMonthFocus, onYearFocus, onDayBlur, onMonthBlur, onYearBlur, ...props }: DateInputProps): import("react/jsx-runtime").JSX.Element;
4
+ displayName: string;
5
+ };
6
+ export default DateInput;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View } from 'react-native';
3
+ import { StyleSheet } from 'react-native-unistyles';
4
+ import { FormField } from '../FormField';
5
+ import DateInputSegment from './DateInputSegment';
6
+ const DateInput = ({ label, helperText, helperIcon, validationStatus = 'initial', validText, invalidText, disabled, readonly, required, hideDay = false, hideMonth = false, hideYear = false, dayPlaceholder = 'DD', monthPlaceholder = 'MM', yearPlaceholder = 'YYYY', dayValue, monthValue, yearValue, onDayChange, onMonthChange, onYearChange, onDayFocus, onMonthFocus, onYearFocus, onDayBlur, onMonthBlur, onYearBlur, ...props }) => {
7
+ return (_jsx(FormField, { label: label, helperText: helperText, helperIcon: helperIcon, validationStatus: validationStatus, validText: validText, invalidText: invalidText, disabled: disabled, readonly: readonly, required: required, style: styles.wrap, ...props, children: _jsxs(View, { style: styles.container, children: [!hideDay && (_jsx(DateInputSegment, { label: "Day", placeholder: dayPlaceholder, value: dayValue, onChange: onDayChange, onFocus: onDayFocus, onBlur: onDayBlur, disabled: disabled, required: required, readonly: readonly, validationStatus: validationStatus, maxLength: 2, testID: "date-input-day" })), !hideMonth && (_jsx(DateInputSegment, { label: "Month", placeholder: monthPlaceholder, value: monthValue, onChange: onMonthChange, onFocus: onMonthFocus, onBlur: onMonthBlur, disabled: disabled, required: required, readonly: readonly, validationStatus: validationStatus, maxLength: 2, testID: "date-input-month" })), !hideYear && (_jsx(DateInputSegment, { label: "Year", placeholder: yearPlaceholder, value: yearValue, onChange: onYearChange, onFocus: onYearFocus, onBlur: onYearBlur, disabled: disabled, required: required, readonly: readonly, validationStatus: validationStatus, maxLength: 4, testID: "date-input-year" }))] }) }));
8
+ };
9
+ DateInput.displayName = 'DateInput';
10
+ const styles = StyleSheet.create(theme => ({
11
+ wrap: {
12
+ gap: theme.components.input.gap,
13
+ },
14
+ container: {
15
+ flexDirection: 'row',
16
+ gap: theme.components.input.date.gap,
17
+ },
18
+ }));
19
+ export default DateInput;
@@ -0,0 +1,79 @@
1
+ import type { TextInputProps } from 'react-native';
2
+ import type { FormFieldBaseProps } from '../FormField/FormField.props';
3
+ export interface DateInputProps extends FormFieldBaseProps {
4
+ /**
5
+ * Whether the day segment is hidden.
6
+ * @default false
7
+ */
8
+ hideDay?: boolean;
9
+ /**
10
+ * Whether the month segment is hidden.
11
+ * @default false
12
+ */
13
+ hideMonth?: boolean;
14
+ /**
15
+ * Whether the year segment is hidden.
16
+ * @default false
17
+ */
18
+ hideYear?: boolean;
19
+ /**
20
+ * Placeholder text for the day segment.
21
+ */
22
+ dayPlaceholder?: string;
23
+ /**
24
+ * Placeholder text for the month segment.
25
+ */
26
+ monthPlaceholder?: string;
27
+ /**
28
+ * Placeholder text for the year segment.
29
+ */
30
+ yearPlaceholder?: string;
31
+ /**
32
+ * The controlled value for the day segment. Must be used with an `onDayChange` handler.
33
+ */
34
+ dayValue?: string;
35
+ /**
36
+ * The controlled value for the month segment. Must be used with an `onMonthChange` handler.
37
+ */
38
+ monthValue?: string;
39
+ /**
40
+ * The controlled value for the year segment. Must be used with an `onYearChange` handler.
41
+ */
42
+ yearValue?: string;
43
+ /**
44
+ * Callback fired when the day value changes.
45
+ */
46
+ onDayChange?: (text: string) => void;
47
+ /**
48
+ * Callback fired when the month value changes.
49
+ */
50
+ onMonthChange?: (text: string) => void;
51
+ /**
52
+ * Callback fired when the year value changes.
53
+ */
54
+ onYearChange?: (text: string) => void;
55
+ /**
56
+ * Callback fired when the day segment receives focus.
57
+ */
58
+ onDayFocus?: TextInputProps['onFocus'];
59
+ /**
60
+ * Callback fired when the month segment receives focus.
61
+ */
62
+ onMonthFocus?: TextInputProps['onFocus'];
63
+ /**
64
+ * Callback fired when the year segment receives focus.
65
+ */
66
+ onYearFocus?: TextInputProps['onFocus'];
67
+ /**
68
+ * Callback fired when the day segment loses focus.
69
+ */
70
+ onDayBlur?: TextInputProps['onBlur'];
71
+ /**
72
+ * Callback fired when the month segment loses focus.
73
+ */
74
+ onMonthBlur?: TextInputProps['onBlur'];
75
+ /**
76
+ * Callback fired when the year segment loses focus.
77
+ */
78
+ onYearBlur?: TextInputProps['onBlur'];
79
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import type { DateInputProps } from './DateInput.props';
2
+ interface DateInputSegmentProps {
3
+ label: string;
4
+ placeholder?: string;
5
+ value?: string;
6
+ onChange?: (text: string) => void;
7
+ onFocus?: DateInputProps['onDayFocus'];
8
+ onBlur?: DateInputProps['onDayBlur'];
9
+ disabled?: boolean;
10
+ required?: boolean;
11
+ validationStatus?: DateInputProps['validationStatus'];
12
+ maxLength?: number;
13
+ readonly?: boolean;
14
+ testID?: string;
15
+ }
16
+ declare const DateInputSegment: {
17
+ ({ label, placeholder, value, onChange, onFocus, onBlur, disabled, validationStatus, maxLength, readonly, testID, }: DateInputSegmentProps): import("react/jsx-runtime").JSX.Element;
18
+ displayName: string;
19
+ };
20
+ export default DateInputSegment;
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View } from 'react-native';
3
+ import { StyleSheet } from 'react-native-unistyles';
4
+ import { BodyText } from '../BodyText';
5
+ import { Input } from '../Input';
6
+ const DateInputSegment = ({ label, placeholder, value, onChange, onFocus, onBlur, disabled, validationStatus, maxLength, readonly, testID, }) => {
7
+ styles.useVariants({ disabled });
8
+ return (_jsxs(View, { style: styles.container, children: [_jsx(BodyText, { size: "md", style: styles.label, children: label }), _jsx(Input, { value: value, onChangeText: onChange, onFocus: onFocus, onBlur: onBlur, placeholder: disabled ? undefined : placeholder, keyboardType: "number-pad", maxLength: maxLength, testID: testID, accessibilityLabel: label, disabled: disabled, validationStatus: validationStatus, readonly: readonly, style: styles.input })] }));
9
+ };
10
+ DateInputSegment.displayName = 'DateInputSegment';
11
+ const styles = StyleSheet.create(theme => ({
12
+ container: {
13
+ flex: 1,
14
+ gap: theme.components.input.gap,
15
+ maxWidth: 96,
16
+ },
17
+ label: {
18
+ variants: {
19
+ disabled: {
20
+ true: {
21
+ opacity: theme.opacity.disabled,
22
+ },
23
+ },
24
+ },
25
+ },
26
+ input: {
27
+ flex: 1,
28
+ maxWidth: 96,
29
+ },
30
+ }));
31
+ export default DateInputSegment;
@@ -0,0 +1,2 @@
1
+ export { default as DateInput } from './DateInput';
2
+ export type { DateInputProps } from './DateInput.props';
@@ -0,0 +1 @@
1
+ export { default as DateInput } from './DateInput';
@@ -0,0 +1,16 @@
1
+ import type { PillProps } from './Pill.props';
2
+ export declare const Pill: import("react").ForwardRefExoticComponent<PillProps & {
3
+ states?: {
4
+ active?: boolean;
5
+ };
6
+ } & Omit<import("react-native").PressableProps, "children"> & {
7
+ tabIndex?: 0 | -1 | undefined;
8
+ } & {
9
+ children?: import("react").ReactNode | (({ hovered, pressed, focused, focusVisible, disabled, }: {
10
+ hovered?: boolean | undefined;
11
+ pressed?: boolean | undefined;
12
+ focused?: boolean | undefined;
13
+ focusVisible?: boolean | undefined;
14
+ disabled?: boolean | undefined;
15
+ }) => import("react").ReactNode);
16
+ } & import("react").RefAttributes<unknown>>;
@@ -0,0 +1,94 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createPressable } from '@gluestack-ui/pressable';
3
+ import { Pressable } from 'react-native';
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+ import { Icon } from '../Icon';
6
+ import { BodyText } from '../BodyText';
7
+ import { usePillGroupContext } from './PillGroup.context';
8
+ const PillRoot = ({ value, label, icon, states = {}, ...props }) => {
9
+ const { active } = states;
10
+ const context = usePillGroupContext();
11
+ const isSelected = context?.value.includes(value) ?? false;
12
+ styles.useVariants({ selected: isSelected, active });
13
+ const handlePress = () => {
14
+ context?.onChange(value);
15
+ };
16
+ return (_jsxs(Pressable, { ...props, style: styles.pill, accessibilityRole: "button", accessibilityState: { selected: isSelected }, onPress: handlePress, children: [icon && _jsx(Icon, { as: icon, size: "sm", style: styles.icon }), _jsx(BodyText, { weight: "semibold", style: styles.text, children: label })] }));
17
+ };
18
+ export const Pill = createPressable({ Root: PillRoot });
19
+ Pill.displayName = 'Pill';
20
+ const styles = StyleSheet.create(theme => ({
21
+ pill: {
22
+ flexDirection: 'row',
23
+ alignItems: 'center',
24
+ justifyContent: 'center',
25
+ height: theme.components.pill.height,
26
+ minWidth: theme.components.pill.minWidth,
27
+ gap: theme.components.pill.gap,
28
+ paddingHorizontal: theme.components.pill.paddingHorizontal,
29
+ paddingVertical: theme.components.pill.paddingVertical,
30
+ borderRadius: theme.components.pill.borderRadius,
31
+ borderWidth: theme.components.pill.borderWidth,
32
+ borderColor: theme.color.interactive.neutral.border.subtle,
33
+ backgroundColor: 'transparent',
34
+ _web: {
35
+ _hover: {
36
+ backgroundColor: theme.color.interactive.neutral.surface.subtle.hover,
37
+ },
38
+ '_focus-visible': theme.helpers.focusVisible,
39
+ },
40
+ variants: {
41
+ active: {
42
+ true: {
43
+ backgroundColor: theme.color.interactive.neutral.surface.subtle.active,
44
+ },
45
+ },
46
+ selected: {
47
+ true: {
48
+ backgroundColor: theme.color.interactive.brand.surface.strong.default,
49
+ borderColor: theme.color.interactive.brand.surface.strong.default,
50
+ _web: {
51
+ _hover: {
52
+ backgroundColor: theme.color.interactive.brand.surface.strong.hover,
53
+ borderColor: theme.color.interactive.brand.surface.strong.hover,
54
+ },
55
+ },
56
+ },
57
+ },
58
+ },
59
+ compoundVariants: [
60
+ {
61
+ selected: true,
62
+ active: true,
63
+ styles: {
64
+ backgroundColor: theme.color.interactive.brand.surface.strong.active,
65
+ borderColor: theme.color.interactive.brand.surface.strong.active,
66
+ },
67
+ },
68
+ ],
69
+ },
70
+ text: {
71
+ variants: {
72
+ selected: {
73
+ true: {
74
+ color: theme.color.text.inverted,
75
+ },
76
+ false: {
77
+ color: theme.color.text.primary,
78
+ },
79
+ },
80
+ },
81
+ },
82
+ icon: {
83
+ variants: {
84
+ selected: {
85
+ true: {
86
+ color: theme.color.icon.inverted,
87
+ },
88
+ false: {
89
+ color: theme.color.icon.primary,
90
+ },
91
+ },
92
+ },
93
+ },
94
+ }));
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { PressableProps } from 'react-native';
3
+ export interface PillProps extends Omit<PressableProps, 'children'> {
4
+ /** Value returned when selected */
5
+ value: string;
6
+ /** Text label shown inside the pill */
7
+ label: string;
8
+ /** Left icon */
9
+ icon?: React.ComponentType<any>;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ export interface PillGroupContextValue {
2
+ value: string[];
3
+ onChange: (value: string) => void;
4
+ }
5
+ export declare const PillGroupContext: import("react").Context<PillGroupContextValue | null>;
6
+ export declare const usePillGroupContext: () => PillGroupContextValue | null;
@@ -0,0 +1,5 @@
1
+ import { createContext, useContext } from 'react';
2
+ export const PillGroupContext = createContext(null);
3
+ export const usePillGroupContext = () => {
4
+ return useContext(PillGroupContext);
5
+ };
@@ -0,0 +1,5 @@
1
+ import type { PillGroupProps } from './PillGroup.props';
2
+ export declare const PillGroup: {
3
+ ({ children, value, multiple, wrap, onChange, style, ...props }: PillGroupProps): import("react/jsx-runtime").JSX.Element;
4
+ displayName: string;
5
+ };
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { ScrollView } from 'react-native';
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+ import { Box } from '../Box';
6
+ import { PillGroupContext } from './PillGroup.context';
7
+ export const PillGroup = ({ children, value, multiple = false, wrap = true, onChange, style, ...props }) => {
8
+ const normalizedValue = Array.isArray(value) ? value : [value];
9
+ const contextValue = useMemo(() => ({
10
+ value: normalizedValue,
11
+ onChange: (pillValue) => {
12
+ if (multiple) {
13
+ const newValue = normalizedValue.includes(pillValue)
14
+ ? normalizedValue.filter(v => v !== pillValue)
15
+ : [...normalizedValue, pillValue];
16
+ onChange?.(newValue);
17
+ }
18
+ else {
19
+ onChange?.(pillValue);
20
+ }
21
+ },
22
+ }), [normalizedValue, multiple, onChange]);
23
+ return (_jsx(PillGroupContext.Provider, { value: contextValue, children: wrap ? (_jsx(Box, { style: [styles.group, styles.wrap, style], ...props, children: children })) : (_jsx(ScrollView, { horizontal: true, contentContainerStyle: [styles.group, style], showsHorizontalScrollIndicator: false, ...props, children: children })) }));
24
+ };
25
+ PillGroup.displayName = 'PillGroup';
26
+ const styles = StyleSheet.create(theme => ({
27
+ group: {
28
+ flexDirection: 'row',
29
+ gap: theme.components.pill.group.gap,
30
+ },
31
+ wrap: {
32
+ flexWrap: 'wrap',
33
+ },
34
+ }));
@@ -0,0 +1,15 @@
1
+ import React from 'react';
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;
8
+ /** Allow pills to wrap lines. Default = true */
9
+ wrap?: boolean;
10
+ /** Handle selection changes */
11
+ onChange?: (value: string | string[]) => void;
12
+ /** Children must be <Pill> elements */
13
+ children: React.ReactNode;
14
+ style?: ViewStyle | ViewStyle[];
15
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export { PillGroup } from './PillGroup';
2
+ export type { PillGroupProps } from './PillGroup.props';
3
+ export { Pill } from './Pill';
4
+ export type { PillProps } from './Pill.props';
@@ -0,0 +1,2 @@
1
+ export { PillGroup } from './PillGroup';
2
+ export { Pill } from './Pill';
@@ -77,7 +77,7 @@ const Select = ({ options = [], value, onValueChange, label, placeholder = 'Sele
77
77
  selectedValue: value,
78
78
  onValueChange,
79
79
  close: closeBottomSheet,
80
- }, children: [menuHeading && (_jsx(View, { style: styles.headingContainer, children: _jsx(DetailText, { size: "lg", children: menuHeading }) })), searchable && (_jsx(View, { style: styles.searchContainer, children: _jsx(Input, { placeholder: searchPlaceholder, value: search, onChangeText: setSearch, type: "search" }) })), children ? (_jsx(BottomSheetScrollView, { children: children })) : (_jsx(BottomSheetFlatList, { data: filteredOptions, keyExtractor: (option) => option.value, renderItem: renderSelectOption, ListEmptyComponent: renderEmptyComponent, ...listProps }))] }) })] }));
80
+ }, children: [menuHeading && (_jsx(View, { style: styles.headingContainer, children: _jsx(DetailText, { size: "lg", children: menuHeading }) })), searchable && (_jsx(View, { style: styles.searchContainer, children: _jsx(Input, { placeholder: searchPlaceholder, value: search, inBottomSheet: true, onChangeText: setSearch, type: "search" }) })), children ? (_jsx(BottomSheetScrollView, { children: children })) : (_jsx(BottomSheetFlatList, { data: filteredOptions, keyExtractor: (option) => option.value, renderItem: renderSelectOption, ListEmptyComponent: renderEmptyComponent, ...listProps }))] }) })] }));
81
81
  };
82
82
  const styles = StyleSheet.create(theme => ({
83
83
  container: {
@@ -161,6 +161,7 @@ const styles = StyleSheet.create(theme => ({
161
161
  emptyContainer: {
162
162
  alignItems: 'center',
163
163
  justifyContent: 'center',
164
+ marginTop: theme.space.md,
164
165
  },
165
166
  }));
166
167
  Select.displayName = 'Select';
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import type { ToastContextValue } from './Toast.props';
3
+ declare const ToastContext: React.Context<ToastContextValue | undefined>;
4
+ export declare const useToastContext: () => ToastContextValue;
5
+ export declare const ToastProvider: React.FC<{
6
+ children: React.ReactNode;
7
+ }>;
8
+ export declare const useToast: () => ToastContextValue;
9
+ export default ToastContext;