react-native-molecules 0.5.0-beta.21 → 0.5.0-beta.23

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 (59) hide show
  1. package/components/Button/Button.tsx +3 -1
  2. package/components/Card/Card.tsx +1 -1
  3. package/components/Checkbox/CheckboxBase.ios.tsx +1 -4
  4. package/components/Checkbox/CheckboxBase.tsx +2 -7
  5. package/components/DatePicker/DateCalendar.tsx +4 -4
  6. package/components/DatePicker/DatePickerModal.tsx +2 -1
  7. package/components/DatePicker/utils.ts +2 -0
  8. package/components/DatePickerInline/DatePickerDockedHeader.tsx +3 -3
  9. package/components/DatePickerInline/DatePickerInline.tsx +1 -1
  10. package/components/DatePickerInline/DatePickerInlineBase.tsx +2 -2
  11. package/components/DatePickerInline/DatePickerInlineHeader.tsx +43 -17
  12. package/components/DatePickerInline/HeaderItem.tsx +2 -2
  13. package/components/DatePickerInline/MonthPicker.tsx +58 -64
  14. package/components/DatePickerInline/Swiper.native.tsx +2 -2
  15. package/components/DatePickerInline/Swiper.tsx +3 -3
  16. package/components/DatePickerInline/YearPicker.tsx +108 -119
  17. package/components/DatePickerInline/{DatePickerContext.tsx → store.tsx} +7 -3
  18. package/components/DatePickerInline/types.ts +1 -1
  19. package/components/Divider/Divider.tsx +192 -0
  20. package/components/Divider/index.tsx +11 -0
  21. package/components/Drawer/DrawerItemGroup.tsx +3 -7
  22. package/components/IconButton/IconButton.tsx +2 -12
  23. package/components/List/List.tsx +275 -0
  24. package/components/List/context.tsx +26 -0
  25. package/components/List/index.ts +8 -0
  26. package/components/List/types.ts +117 -0
  27. package/components/List/utils.ts +79 -0
  28. package/components/Menu/Menu.tsx +146 -19
  29. package/components/Menu/index.tsx +9 -7
  30. package/components/Menu/utils.ts +21 -70
  31. package/components/Popover/Popover.tsx +7 -10
  32. package/components/Popover/PopoverRoot.tsx +6 -20
  33. package/components/Popover/common.ts +4 -0
  34. package/components/Popover/index.ts +2 -8
  35. package/components/Popover/usePlatformMeasure.ts +4 -2
  36. package/components/RadioButton/RadioButtonAndroid.tsx +38 -54
  37. package/components/RadioButton/RadioButtonIOS.tsx +2 -16
  38. package/components/Select/Select.tsx +307 -501
  39. package/components/Select/context.tsx +39 -32
  40. package/components/Select/types.ts +63 -56
  41. package/components/Select/utils.ts +19 -44
  42. package/components/Text/textFactory.tsx +17 -5
  43. package/components/TimePicker/TimeInput.tsx +2 -7
  44. package/components/TimePicker/utils.ts +0 -4
  45. package/components/TouchableRipple/TouchableRipple.native.tsx +36 -5
  46. package/components/TouchableRipple/TouchableRipple.tsx +121 -163
  47. package/components/TouchableRipple/rippleFromForegroundColor.ts +21 -0
  48. package/package.json +6 -3
  49. package/components/HorizontalDivider/HorizontalDivider.tsx +0 -103
  50. package/components/HorizontalDivider/index.tsx +0 -9
  51. package/components/ListItem/ListItem.tsx +0 -138
  52. package/components/ListItem/ListItemDescription.tsx +0 -25
  53. package/components/ListItem/ListItemTitle.tsx +0 -25
  54. package/components/ListItem/index.tsx +0 -14
  55. package/components/ListItem/utils.ts +0 -115
  56. package/components/Menu/MenuDivider.tsx +0 -13
  57. package/components/Menu/MenuItem.tsx +0 -128
  58. package/components/VerticalDivider/VerticalDivider.tsx +0 -100
  59. package/components/VerticalDivider/index.tsx +0 -9
@@ -1,15 +1,17 @@
1
1
  import { getRegisteredComponentWithFallback } from '../../core';
2
- import MenuComponent from './Menu';
3
- import MenuDivider from './MenuDivider';
4
- import MenuItem from './MenuItem';
2
+ import { List } from '../List';
3
+ import MenuComponent, { MenuItem, MenuRoot, MenuTrigger } from './Menu';
4
+ import { MenuRootContext } from './utils';
5
5
 
6
6
  export const MenuDefault = Object.assign(MenuComponent, {
7
+ Root: MenuRoot,
8
+ Trigger: MenuTrigger,
7
9
  Item: MenuItem,
8
- Divider: MenuDivider,
10
+ Content: List.Content,
11
+ RootContext: MenuRootContext,
9
12
  });
10
13
 
11
14
  export const Menu = getRegisteredComponentWithFallback('Menu', MenuDefault);
12
15
 
13
- export type { Props as MenuProps } from './Menu';
14
- export type { Props as MenuItemProps } from './MenuItem';
15
- export { menuItemStyles, menuStyles } from './utils';
16
+ export type { MenuItemProps, Props as MenuProps, MenuRootProps, MenuTriggerProps } from './Menu';
17
+ export { MenuRootContext, menuStyles } from './utils';
@@ -1,7 +1,28 @@
1
+ import { createContext, type RefObject } from 'react';
2
+ import type { View } from 'react-native';
1
3
  import { StyleSheet } from 'react-native-unistyles';
2
4
 
3
5
  import { getRegisteredComponentStylesWithFallback } from '../../core';
4
6
 
7
+ export const MenuContext = createContext({
8
+ closeOnSelect: true,
9
+ onClose: () => {},
10
+ });
11
+
12
+ export type MenuRootContextValue = {
13
+ isOpen: boolean;
14
+ onOpen: () => void;
15
+ onClose: () => void;
16
+ triggerRef: RefObject<View | any>;
17
+ };
18
+
19
+ export const MenuRootContext = createContext<MenuRootContextValue>({
20
+ isOpen: false,
21
+ onOpen: () => {},
22
+ onClose: () => {},
23
+ triggerRef: { current: null },
24
+ });
25
+
5
26
  const menuStylesDefault = StyleSheet.create(theme => ({
6
27
  root: {
7
28
  paddingVertical: theme.spacings['2'],
@@ -17,74 +38,4 @@ const menuStylesDefault = StyleSheet.create(theme => ({
17
38
  },
18
39
  }));
19
40
 
20
- const menuItemStylesDefault = StyleSheet.create(theme => ({
21
- root: {
22
- paddingVertical: theme.spacings['2'],
23
- flexDirection: 'row',
24
- alignItems: 'center',
25
-
26
- variants: {
27
- state: {
28
- disabled: {
29
- opacity: 0.38,
30
- },
31
- hovered: {},
32
- },
33
- size: {
34
- default: {
35
- paddingHorizontal: theme.spacings['4'],
36
- height: 48,
37
- },
38
-
39
- dense: {
40
- paddingLeft: theme.spacings['4'],
41
- paddingRight: theme.spacings['2'],
42
- height: 32,
43
- },
44
- },
45
- },
46
- },
47
-
48
- text: {
49
- flex: 1,
50
- color: theme.colors.onSurface,
51
-
52
- size: {
53
- default: {
54
- ...theme.typescale.bodyLarge,
55
- },
56
-
57
- dense: {
58
- ...theme.typescale.bodyMedium,
59
- },
60
- },
61
- },
62
-
63
- leftElement: {
64
- marginRight: theme.spacings['5'],
65
- marginLeft: theme.spacings['2'],
66
- },
67
- rightElement: {
68
- marginRight: theme.spacings['2'],
69
- marginLeft: theme.spacings['5'],
70
- },
71
-
72
- stateLayer: {
73
- variants: {
74
- state: {
75
- disabled: {
76
- backgroundColor: 'transparent',
77
- },
78
- hovered: {
79
- backgroundColor: theme.colors.stateLayer.hover.onSurface,
80
- },
81
- },
82
- },
83
- },
84
- }));
85
-
86
41
  export const menuStyles = getRegisteredComponentStylesWithFallback('Menu', menuStylesDefault);
87
- export const menuItemStyles = getRegisteredComponentStylesWithFallback(
88
- 'Menu_Item',
89
- menuItemStylesDefault,
90
- );
@@ -27,9 +27,7 @@ import { createPopoverRoot } from './PopoverRoot';
27
27
  import { usePlatformMeasure } from './usePlatformMeasure';
28
28
  import { popoverStyles } from './utils';
29
29
 
30
- type PopoverPanelProps = PopoverProps & {
31
- overlay?: ReactNode;
32
- };
30
+ type PopoverPanelProps = PopoverProps & { backdrop?: ReactNode };
33
31
 
34
32
  const PopoverPanel = ({
35
33
  triggerRef,
@@ -45,7 +43,7 @@ const PopoverPanel = ({
45
43
  offset = 8,
46
44
  horizontalOffset = 0,
47
45
  triggerDimensions,
48
- overlay,
46
+ backdrop,
49
47
  ...rest
50
48
  }: PopoverPanelProps) => {
51
49
  const {
@@ -58,11 +56,13 @@ const PopoverPanel = ({
58
56
  } = usePopover({ isOpen, position, align, offset, horizontalOffset });
59
57
 
60
58
  const popoverRef = useRef<View>(null);
59
+ const hasBackdrop = !!backdrop;
61
60
 
62
61
  const { popoverStyle } = usePlatformMeasure({
63
62
  triggerRef,
64
63
  isOpen,
65
64
  onClose,
65
+ dismissOnClickOutside: !hasBackdrop,
66
66
  calculatedPosition,
67
67
  calculateAndSetPosition,
68
68
  targetLayoutRef,
@@ -93,8 +93,8 @@ const PopoverPanel = ({
93
93
  return (
94
94
  <Portal>
95
95
  <PopoverPanelContext value={panelContextValue}>
96
+ {backdrop}
96
97
  <Wrapper {...(WrapperProps as any)}>
97
- {overlay}
98
98
  <View
99
99
  onLayout={handlePopoverLayout}
100
100
  style={[popoverStyles.popoverContainer, style, popoverStyle]}
@@ -127,10 +127,7 @@ export const PopoverTrigger = memo(
127
127
  );
128
128
  PopoverTrigger.displayName = 'Popover_Trigger';
129
129
 
130
- export const PopoverContent = memo(({ children }: { children?: ReactNode }) => <>{children}</>);
131
- PopoverContent.displayName = 'Popover_Content';
132
-
133
- export const PopoverOverlay = memo(({ style, onPress, ...rest }: PressableProps) => {
130
+ export const PopoverBackdrop = memo(({ style, onPress, ...rest }: PressableProps) => {
134
131
  const { isOpen, onClose } = useContext(PopoverContext);
135
132
  if (!isOpen) return null;
136
133
  return (
@@ -141,7 +138,7 @@ export const PopoverOverlay = memo(({ style, onPress, ...rest }: PressableProps)
141
138
  />
142
139
  );
143
140
  });
144
- PopoverOverlay.displayName = 'Popover_Overlay';
141
+ PopoverBackdrop.displayName = 'Popover_Backdrop';
145
142
 
146
143
  type PopoverArrowProps = {
147
144
  size?: number;
@@ -1,17 +1,10 @@
1
- import {
2
- type ComponentType,
3
- memo,
4
- type ReactElement,
5
- type ReactNode,
6
- useMemo,
7
- useRef,
8
- } from 'react';
1
+ import { type ComponentType, memo, type ReactNode, useMemo, useRef } from 'react';
9
2
  import { type View } from 'react-native';
10
3
 
11
4
  import { extractSubcomponents } from '../../utils/extractSubcomponents';
12
5
  import { PopoverContext, type PopoverProps } from './common';
13
6
 
14
- type PopoverPanelInternalProps = PopoverProps & { overlay?: ReactNode };
7
+ type PopoverPanelInternalProps = PopoverProps & { backdrop?: ReactNode };
15
8
 
16
9
  export const createPopoverRoot = (PopoverPanel: ComponentType<PopoverPanelInternalProps>) => {
17
10
  const PopoverRoot = ({
@@ -25,15 +18,13 @@ export const createPopoverRoot = (PopoverPanel: ComponentType<PopoverPanelIntern
25
18
 
26
19
  const {
27
20
  Popover_Trigger,
28
- Popover_Content,
29
- Popover_Overlay,
21
+ Popover_Backdrop,
30
22
  rest: restChildren,
31
23
  } = extractSubcomponents({
32
24
  children,
33
25
  allowedChildren: [
34
26
  { name: 'Popover_Trigger', allowMultiple: false },
35
- { name: 'Popover_Content', allowMultiple: false },
36
- { name: 'Popover_Overlay', allowMultiple: false },
27
+ { name: 'Popover_Backdrop', allowMultiple: false },
37
28
  ] as const,
38
29
  includeRest: true,
39
30
  });
@@ -41,11 +32,6 @@ export const createPopoverRoot = (PopoverPanel: ComponentType<PopoverPanelIntern
41
32
  const hasTrigger = Popover_Trigger.length > 0;
42
33
  const resolvedTriggerRef = triggerRefProp ?? (hasTrigger ? internalTriggerRef : undefined);
43
34
 
44
- const panelContent =
45
- Popover_Content.length > 0
46
- ? (Popover_Content[0] as ReactElement<{ children?: ReactNode }>).props.children
47
- : restChildren;
48
-
49
35
  const contextValue = useMemo(
50
36
  () => ({
51
37
  triggerRef: resolvedTriggerRef ?? internalTriggerRef,
@@ -62,9 +48,9 @@ export const createPopoverRoot = (PopoverPanel: ComponentType<PopoverPanelIntern
62
48
  triggerRef={resolvedTriggerRef}
63
49
  isOpen={isOpen}
64
50
  onClose={onClose}
65
- overlay={Popover_Overlay[0]}
51
+ backdrop={Popover_Backdrop[0]}
66
52
  {...rest}>
67
- {panelContent}
53
+ {restChildren}
68
54
  </PopoverPanel>
69
55
  </PopoverContext>
70
56
  );
@@ -4,6 +4,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import type { LayoutRectangle, StyleProp, View, ViewStyle } from 'react-native';
5
5
  import { Dimensions, StyleSheet } from 'react-native';
6
6
 
7
+ import { registerPortalContext } from '../Portal';
8
+
7
9
  export type Position = 'top' | 'left' | 'right' | 'bottom';
8
10
  export type Align = 'start' | 'center' | 'end';
9
11
 
@@ -27,6 +29,8 @@ export const PopoverContext = createContext<PopoverContextValue>({
27
29
  triggerRef: { current: null },
28
30
  });
29
31
 
32
+ registerPortalContext(PopoverContext);
33
+
30
34
  export type PopoverPanelContextValue = {
31
35
  calculatedPosition: ViewStyle | null;
32
36
  targetLayoutRef: RefObject<LayoutRectangle | null>;
@@ -1,18 +1,12 @@
1
1
  import { getRegisteredComponentWithFallback } from '../../core';
2
- import PopoverDefault, {
3
- PopoverArrow,
4
- PopoverContent,
5
- PopoverOverlay,
6
- PopoverTrigger,
7
- } from './Popover';
2
+ import PopoverDefault, { PopoverArrow, PopoverBackdrop, PopoverTrigger } from './Popover';
8
3
 
9
4
  const PopoverBase = getRegisteredComponentWithFallback('Popover', PopoverDefault);
10
5
 
11
6
  export const Popover = Object.assign(PopoverBase, {
12
7
  Trigger: PopoverTrigger,
13
- Content: PopoverContent,
14
8
  Arrow: PopoverArrow,
15
- Overlay: PopoverOverlay,
9
+ Backdrop: PopoverBackdrop,
16
10
  });
17
11
 
18
12
  export type { Align, PopoverProps, Position } from './common';
@@ -7,6 +7,7 @@ export type UsePlatformMeasureArgs = {
7
7
  triggerRef: RefObject<View | any> | undefined;
8
8
  isOpen: boolean;
9
9
  onClose?: () => void;
10
+ dismissOnClickOutside?: boolean;
10
11
  calculatedPosition: ViewStyle | null;
11
12
  calculateAndSetPosition: () => void;
12
13
  targetLayoutRef: RefObject<LayoutRectangle | null>;
@@ -23,6 +24,7 @@ export const usePlatformMeasure = ({
23
24
  triggerRef,
24
25
  isOpen,
25
26
  onClose,
27
+ dismissOnClickOutside = true,
26
28
  calculatedPosition,
27
29
  calculateAndSetPosition,
28
30
  targetLayoutRef,
@@ -82,7 +84,7 @@ export const usePlatformMeasure = ({
82
84
  }, [isOpen, measureTarget, triggerRef]);
83
85
 
84
86
  useEffect(() => {
85
- if (!isOpen || !onClose) return;
87
+ if (!isOpen || !onClose || !dismissOnClickOutside) return;
86
88
  const handleClickOutside = (event: MouseEvent) => {
87
89
  const popoverElement = popoverRef.current as any as HTMLElement;
88
90
  const targetElement = triggerRef?.current as any as HTMLElement;
@@ -99,7 +101,7 @@ export const usePlatformMeasure = ({
99
101
  return () => {
100
102
  document.removeEventListener('mousedown', handleClickOutside, { capture: true });
101
103
  };
102
- }, [isOpen, onClose, popoverRef, triggerRef]);
104
+ }, [dismissOnClickOutside, isOpen, onClose, popoverRef, triggerRef]);
103
105
 
104
106
  const popoverStyle = useMemo(() => {
105
107
  if (!calculatedPosition) return popoverDefaultStyles;
@@ -1,4 +1,3 @@
1
- import setColor from 'color';
2
1
  import { forwardRef, memo, type PropsWithoutRef, useEffect, useMemo, useRef } from 'react';
3
2
  import type { ViewProps } from 'react-native';
4
3
  import { Animated, StyleSheet, View } from 'react-native';
@@ -77,58 +76,44 @@ const RadioButtonAndroid = (
77
76
  state: state as any,
78
77
  });
79
78
 
80
- const {
81
- containerStyles,
82
- rippleColor,
83
- radioStyles,
84
- dotStyles,
85
- dotContainerStyles,
86
- stateLayerStyle,
87
- } = useMemo(() => {
88
- const _color = tokenStylesParser.getColor(checked ? colorProp : uncheckedColorProp);
89
-
90
- let _rippleColor: string | undefined;
91
-
92
- try {
93
- _rippleColor = setColor(_color).alpha(0.32).rgb().string();
94
- } catch (e) {
95
- _rippleColor = undefined;
96
- }
97
-
98
- return {
99
- containerStyles: [radioButtonStyles.container, radioButtonStyles.root, style],
100
- rippleColor: _rippleColor,
101
- radioStyles: [
102
- radioButtonStyles.radio,
103
- {
104
- borderWidth: borderAnim,
105
- },
106
- tokenStylesParser.getColor(checked ? colorProp : uncheckedColorProp, 'borderColor'),
107
- ],
108
- dotContainerStyles: [StyleSheet.absoluteFill, radioButtonStyles.radioContainer],
109
- dotStyles: [
110
- radioButtonStyles.dot,
111
- {
112
- transform: [{ scale: radioAnim }],
113
- },
114
- tokenStylesParser.getColor(
115
- checked ? colorProp : uncheckedColorProp,
116
- 'backgroundColor',
117
- ),
118
- ],
119
- stateLayerStyle: [radioButtonStyles.stateLayer, stateLayerProps?.style],
120
- };
121
- // eslint-disable-next-line react-hooks/exhaustive-deps
122
- }, [
123
- borderAnim,
124
- checked,
125
- colorProp,
126
- radioAnim,
127
- stateLayerProps?.style,
128
- uncheckedColorProp,
129
- style,
130
- state,
131
- ]);
79
+ const { containerStyles, radioStyles, dotStyles, dotContainerStyles, stateLayerStyle } =
80
+ useMemo(() => {
81
+ return {
82
+ containerStyles: [radioButtonStyles.container, radioButtonStyles.root, style],
83
+ radioStyles: [
84
+ radioButtonStyles.radio,
85
+ {
86
+ borderWidth: borderAnim,
87
+ },
88
+ tokenStylesParser.getColor(
89
+ checked ? colorProp : uncheckedColorProp,
90
+ 'borderColor',
91
+ ),
92
+ ],
93
+ dotContainerStyles: [StyleSheet.absoluteFill, radioButtonStyles.radioContainer],
94
+ dotStyles: [
95
+ radioButtonStyles.dot,
96
+ {
97
+ transform: [{ scale: radioAnim }],
98
+ },
99
+ tokenStylesParser.getColor(
100
+ checked ? colorProp : uncheckedColorProp,
101
+ 'backgroundColor',
102
+ ),
103
+ ],
104
+ stateLayerStyle: [radioButtonStyles.stateLayer, stateLayerProps?.style],
105
+ };
106
+ // eslint-disable-next-line react-hooks/exhaustive-deps
107
+ }, [
108
+ borderAnim,
109
+ checked,
110
+ colorProp,
111
+ radioAnim,
112
+ stateLayerProps?.style,
113
+ uncheckedColorProp,
114
+ style,
115
+ state,
116
+ ]);
132
117
 
133
118
  useEffect(() => {
134
119
  // Do not run animation on very first rendering
@@ -160,7 +145,6 @@ const RadioButtonAndroid = (
160
145
  <TouchableRipple
161
146
  {...rest}
162
147
  ref={actionsRef}
163
- rippleColor={rippleColor}
164
148
  onPress={onPress}
165
149
  style={containerStyles}
166
150
  testID={testID}>
@@ -1,4 +1,3 @@
1
- import setColor from 'color';
2
1
  import { forwardRef, memo, useMemo } from 'react';
3
2
  import { StyleSheet, View } from 'react-native';
4
3
 
@@ -56,32 +55,19 @@ const RadioButtonIOS = (
56
55
  state: state as any,
57
56
  });
58
57
 
59
- const { containerStyle, iconContainerStyle, iconStyle, rippleColor } = useMemo(() => {
58
+ const { containerStyle, iconContainerStyle, iconStyle } = useMemo(() => {
60
59
  const _color = tokenStylesParser.getColor(checked ? colorProp : uncheckedColorProp);
61
- let _rippleColor: string | undefined;
62
-
63
- try {
64
- _rippleColor = setColor(_color).alpha(0.32).rgb().string();
65
- } catch (e) {
66
- _rippleColor = undefined;
67
- }
68
60
 
69
61
  return {
70
62
  containerStyle: [styles.container, radioButtonStyles.root, style],
71
63
  iconContainerStyle: { opacity: checked ? 1 : 0 },
72
64
  iconStyle: [radioButtonStyles.icon, _color],
73
- rippleColor: _rippleColor,
74
65
  };
75
66
  // eslint-disable-next-line react-hooks/exhaustive-deps
76
67
  }, [checked, colorProp, style, state, uncheckedColorProp]);
77
68
 
78
69
  return (
79
- <TouchableRipple
80
- {...rest}
81
- ref={ref}
82
- rippleColor={rippleColor}
83
- onPress={onPress}
84
- style={containerStyle}>
70
+ <TouchableRipple {...rest} ref={ref} onPress={onPress} style={containerStyle}>
85
71
  <View style={iconContainerStyle}>
86
72
  <Icon
87
73
  allowFontScaling={false}