react-native-molecules 0.5.0-beta.10 → 0.5.0-beta.11

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.
@@ -1,15 +1,6 @@
1
1
  import setColor from 'color';
2
- import {
3
- forwardRef,
4
- memo,
5
- type PropsWithoutRef,
6
- useCallback,
7
- useEffect,
8
- useMemo,
9
- useRef,
10
- } from 'react';
11
- import { Animated, Platform, View, type ViewProps } from 'react-native';
12
- import { StyleSheet } from 'react-native-unistyles';
2
+ import { forwardRef, memo, type PropsWithoutRef, useCallback, useMemo } from 'react';
3
+ import { Platform, type ViewProps } from 'react-native';
13
4
 
14
5
  import { useActionState } from '../../hooks';
15
6
  import { resolveStateVariant } from '../../utils';
@@ -44,9 +35,6 @@ const CheckboxAndroid = (
44
35
  }: Props,
45
36
  ref: any,
46
37
  ) => {
47
- const { current: scaleAnim } = useRef<Animated.Value>(new Animated.Value(1));
48
- const isFirstRendering = useRef<boolean>(true);
49
-
50
38
  const { actionsRef, hovered } = useActionState({ ref, actionsToListen: ['hover'] });
51
39
 
52
40
  const state = resolveStateVariant({
@@ -63,101 +51,20 @@ const CheckboxAndroid = (
63
51
  size,
64
52
  });
65
53
 
66
- // const componentStyles = useComponentStyles('Checkbox', style, {
67
- // variant: 'android',
68
- // state: resolveStateVariant({
69
- // disabled,
70
- // checkedAndHovered: checked && !indeterminate && hovered,
71
- // checked: checked && !indeterminate,
72
- // hovered,
73
- // }),
74
- // size,
75
- // });
76
-
77
- const borderWidth = scaleAnim.interpolate({
78
- inputRange: [0.8, 1],
79
- outputRange: [7, 0],
80
- });
81
-
82
- const {
83
- iconSize,
84
- rippleColor,
85
- scale,
86
- animationDuration,
87
- rippleContainerStyles,
88
- filledContainerStyles,
89
- animatedContainerStyles,
90
- animatedFillStyles,
91
- stateLayerStyle,
92
- iconStyle,
93
- } = useMemo(() => {
94
- // const {
95
- // color: checkedColor,
96
- // uncheckedColor,
97
- // animationScale: _scale,
98
- // animationDuration: _animationDuration,
99
- // iconSize: _iconSize,
100
- // padding,
101
- // width,
102
- // height,
103
- // borderRadius,
104
- // ...checkboxStyles
105
- // // @ts-ignore
106
- // } = styles.root;
107
-
108
- const _color = tokenStylesParser.getColor(checked ? colorProp : uncheckedColorProp);
109
-
110
- return {
111
- iconStyle: [styles.icon, _color],
112
- iconSize: iconSizeMap[size],
113
- // TODO - fix this on web
114
- rippleColor:
115
- Platform.OS === 'web' ? undefined : setColor(_color).fade(0.32).rgb().string(),
116
- checkboxStyle: [styles.root, style],
117
- scale: 1,
118
- animationDuration: 100,
119
- rippleContainerStyles: [styles.root],
120
- animatedContainerStyles: { transform: [{ scale: scaleAnim }] },
121
- filledContainerStyles: [StyleSheet.absoluteFill, styles.fillContainer],
122
- // for toggle animation // This needs to be computed because it's opinionated animation
123
- animatedFillStyles: [
124
- styles.animatedFill, // 4 because padding - border(which is 1px each side)
125
- tokenStylesParser.getColor(checked ? colorProp : uncheckedColorProp, 'borderColor'),
126
- { borderWidth },
127
- ],
128
- stateLayerStyle: [styles.stateLayer, stateLayerProps?.style],
129
- };
130
- }, [
131
- borderWidth,
132
- checked,
133
- colorProp,
134
- scaleAnim,
135
- stateLayerProps?.style,
136
- style,
137
- uncheckedColorProp,
138
- size,
139
- ]);
140
-
141
- useEffect(() => {
142
- // Do not run animation on very first rendering
143
- if (isFirstRendering.current) {
144
- isFirstRendering.current = false;
145
- return;
146
- }
147
-
148
- Animated.sequence([
149
- Animated.timing(scaleAnim, {
150
- toValue: 0.85,
151
- duration: checked ? animationDuration * scale : 0,
152
- useNativeDriver: false,
153
- }),
154
- Animated.timing(scaleAnim, {
155
- toValue: 1,
156
- duration: checked ? animationDuration * scale : animationDuration * scale * 1.75,
157
- useNativeDriver: false,
158
- }),
159
- ]).start();
160
- }, [checked, scaleAnim, scale, animationDuration]);
54
+ const { iconSize, rippleColor, rippleContainerStyles, stateLayerStyle, iconStyle } =
55
+ useMemo(() => {
56
+ const _color = tokenStylesParser.getColor(checked ? colorProp : uncheckedColorProp);
57
+
58
+ return {
59
+ iconStyle: [styles.icon, _color],
60
+ iconSize: iconSizeMap[size],
61
+ // TODO - fix this on web
62
+ rippleColor:
63
+ Platform.OS === 'web' ? undefined : setColor(_color).fade(0.32).rgb().string(),
64
+ rippleContainerStyles: [styles.root, style],
65
+ stateLayerStyle: [styles.stateLayer, stateLayerProps?.style],
66
+ };
67
+ }, [checked, colorProp, uncheckedColorProp, size, style, stateLayerProps?.style]);
161
68
 
162
69
  const onChange = useCallback(() => {
163
70
  onChangeProp?.(!checked);
@@ -185,18 +92,13 @@ const CheckboxAndroid = (
185
92
  testID={testID}
186
93
  ref={actionsRef}>
187
94
  <>
188
- <Animated.View style={animatedContainerStyles}>
189
- <Icon
190
- allowFontScaling={false}
191
- type="material-community"
192
- name={icon}
193
- size={iconSize}
194
- style={iconStyle}
195
- />
196
- <View style={filledContainerStyles}>
197
- <Animated.View style={animatedFillStyles} />
198
- </View>
199
- </Animated.View>
95
+ <Icon
96
+ allowFontScaling={false}
97
+ type="material-community"
98
+ name={icon}
99
+ size={iconSize}
100
+ style={iconStyle}
101
+ />
200
102
  <StateLayer
201
103
  testID={testID ? `${testID}-stateLayer` : ''}
202
104
  {...stateLayerProps}
@@ -207,11 +109,4 @@ const CheckboxAndroid = (
207
109
  );
208
110
  };
209
111
 
210
- // const styles = StyleSheet.create({
211
- // fillContainer: {
212
- // alignItems: 'center',
213
- // justifyContent: 'center',
214
- // },
215
- // });
216
-
217
112
  export default memo(forwardRef(CheckboxAndroid));
@@ -132,31 +132,6 @@ const checkboxStylesDefault = StyleSheet.create(theme => ({
132
132
  },
133
133
  ],
134
134
  },
135
- fillContainer: {
136
- alignItems: 'center',
137
- justifyContent: 'center',
138
- },
139
- animatedFill: {
140
- width: 24 / 2 + (PADDING - 2),
141
- height: 24 / 2 + (PADDING - 2),
142
-
143
- variants: {
144
- size: {
145
- sm: {
146
- width: iconSizeMap.sm / 2 + (PADDING - 2),
147
- height: iconSizeMap.sm / 2 + (PADDING - 2),
148
- },
149
- md: {
150
- width: iconSizeMap.md / 2 + (PADDING - 2),
151
- height: iconSizeMap.md / 2 + (PADDING - 2),
152
- },
153
- lg: {
154
- width: iconSizeMap.lg / 2 + (PADDING - 2),
155
- height: iconSizeMap.lg / 2 + (PADDING - 2),
156
- },
157
- },
158
- },
159
- },
160
135
  icon: {
161
136
  color: theme.colors.onSurfaceVariant,
162
137
 
@@ -5,6 +5,7 @@ import { useLatest, useToggle } from '../../hooks';
5
5
  import { noop } from '../../utils/lodash';
6
6
  import { DatePickerDocked } from '../DatePickerDocked';
7
7
  import { IconButton } from '../IconButton';
8
+ import { TextInput } from '../TextInput';
8
9
  import DatePickerInputModal from './DatePickerInputModal';
9
10
  import DatePickerInputWithoutModal from './DatePickerInputWithoutModal';
10
11
  import type { DatePickerInputProps } from './types';
@@ -125,8 +126,9 @@ function DatePickerInput(
125
126
  validRange={validRange}
126
127
  onChange={onChange}
127
128
  // locale={locale}
128
- inputButtons={rightElement}
129
- />
129
+ >
130
+ <TextInput.Right>{rightElement}</TextInput.Right>
131
+ </DatePickerInputWithoutModal>
130
132
  );
131
133
  }
132
134
 
@@ -7,13 +7,11 @@ import { datePickerInputStyles } from './utils';
7
7
 
8
8
  function DatePickerInputWithoutModal(
9
9
  {
10
- label,
11
10
  value,
12
11
  onChange: onChangeProp,
13
12
  // locale = 'en',
14
13
  validRange,
15
14
  inputMode,
16
- inputButtons,
17
15
  dateFormat = 'dd/MM/yyyy',
18
16
  style,
19
17
  onBlur: onBlurProp,
@@ -61,14 +59,12 @@ function DatePickerInputWithoutModal(
61
59
  onBlur={onBlur}
62
60
  onFocus={onFocus}
63
61
  ref={inputRef}
64
- label={label}
65
62
  value={formattedValue}
66
63
  keyboardType={'number-pad'}
67
64
  mask={dateFormat}
68
65
  onChangeText={onChangeText}
69
66
  // keyboardAppearance={theme.dark ? 'dark' : 'default'}
70
67
  // error={formattedValue ? !!error : false}
71
- right={inputButtons}
72
68
  // supportingText={formattedValue ? error || undefined : undefined}
73
69
  />
74
70
  );
@@ -23,6 +23,4 @@ export interface DatePickerInputProps
23
23
  endYear?: number;
24
24
  dockedPopoverContentProps?: ViewProps;
25
25
  }
26
- export interface DatePickerInputWithoutModalProps extends Omit<DatePickerInputProps, 'withModal'> {
27
- inputButtons?: any;
28
- }
26
+ export interface DatePickerInputWithoutModalProps extends Omit<DatePickerInputProps, 'withModal'> {}
@@ -3,6 +3,7 @@ import { Keyboard, TextInput as TextInputNative, View } from 'react-native';
3
3
 
4
4
  import type { ModeType, ValidRangeType } from '../DatePickerInline';
5
5
  import DatePickerInputWithoutModal from '../DatePickerInput/DatePickerInputWithoutModal';
6
+ import { TextInput } from '../TextInput';
6
7
  import type { LocalState, LocalStateRange, LocalStateSingle } from './types';
7
8
  import { datePickerModalEditStyles } from './utils';
8
9
 
@@ -91,14 +92,14 @@ Props) {
91
92
  <DatePickerInputWithoutModal
92
93
  inputMode="start"
93
94
  ref={dateInput}
94
- label={label}
95
95
  value={(state as LocalStateSingle).date}
96
96
  onChange={onSingleInputChange}
97
97
  onSubmitEditing={onSubmitInput}
98
98
  validRange={validRange}
99
99
  // locale={locale}
100
- autoComplete={'off'}
101
- />
100
+ autoComplete={'off'}>
101
+ <TextInput.Label>{label}</TextInput.Label>
102
+ </DatePickerInputWithoutModal>
102
103
  ) : null}
103
104
  </>
104
105
 
@@ -108,27 +109,27 @@ Props) {
108
109
  <DatePickerInputWithoutModal
109
110
  inputMode="start"
110
111
  ref={startInput}
111
- label={startLabel}
112
112
  value={(state as LocalStateRange).startDate}
113
113
  onChange={onStartDateChange}
114
114
  returnKeyType={'next'}
115
115
  onSubmitEditing={onSubmitStartInput}
116
116
  validRange={validRange}
117
117
  // locale={locale}
118
- autoComplete={'off'}
119
- />
118
+ autoComplete={'off'}>
119
+ <TextInput.Label>{startLabel}</TextInput.Label>
120
+ </DatePickerInputWithoutModal>
120
121
  <View style={datePickerModalEditStyles.separator} />
121
122
  <DatePickerInputWithoutModal
122
123
  inputMode="end"
123
124
  ref={endInput}
124
- label={endLabel}
125
125
  value={(state as LocalStateRange).endDate}
126
126
  onChange={onEndDateChange}
127
127
  onSubmitEditing={onSubmitEndInput}
128
128
  validRange={validRange}
129
129
  // locale={locale}
130
- autoComplete="off"
131
- />
130
+ autoComplete="off">
131
+ <TextInput.Label>{endLabel}</TextInput.Label>
132
+ </DatePickerInputWithoutModal>
132
133
  </View>
133
134
  ) : null}
134
135
  </>
@@ -1,8 +1,8 @@
1
- import { forwardRef, memo, type ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
1
+ import { memo, useCallback, useMemo } from 'react';
2
2
 
3
+ import useControlledValue from '../../hooks/useControlledValue';
3
4
  import useFilePicker from '../../hooks/useFilePicker';
4
5
  import type { DocumentPickerOptions, DocumentResult } from '../../utils/DocumentPicker';
5
- import { ActivityIndicator } from '../ActivityIndicator';
6
6
  import { IconButton } from '../IconButton';
7
7
  import { TextInput, type TextInputProps } from '../TextInput';
8
8
 
@@ -10,6 +10,7 @@ export type OmitProp =
10
10
  | 'editable'
11
11
  | 'multiline'
12
12
  | 'onChangeText'
13
+ | 'onChange'
13
14
  | 'keyboardType'
14
15
  | 'defaultValue'
15
16
  | 'value'
@@ -24,12 +25,9 @@ export type Props = Omit<TextInputProps, OmitProp> &
24
25
  */
25
26
  multiple?: boolean;
26
27
  /**
27
- * Displays the Spinner on the right side in place of the default upload icon
28
- * @default false
28
+ * Default value for uncontrolled usage
29
29
  */
30
- loading?: boolean;
31
- left?: ReactNode;
32
- right?: ReactNode;
30
+ defaultValue?: DocumentResult | DocumentResult[];
33
31
  /**
34
32
  * To Control the value
35
33
  */
@@ -37,32 +35,31 @@ export type Props = Omit<TextInputProps, OmitProp> &
37
35
  /**
38
36
  * The Callback function to return the selected files as an array or object
39
37
  */
40
- onChange?: (result: DocumentResult | DocumentResult[]) => any;
41
- /**
42
- * To replace the default progress indicator
43
- */
44
- progressIndicator?: ReactNode;
38
+ onChange?: (result: DocumentResult | DocumentResult[] | undefined) => any;
45
39
  };
46
40
 
47
- const FilePicker = (
48
- {
49
- loading,
50
- right: rightProp,
51
- progressIndicator,
52
- type,
53
- multiple,
54
- transitionStyle,
55
- mode,
56
- presentationStyle,
57
- value,
58
- onChange = () => {},
59
- onCancel,
60
- onError,
61
- ...rest
62
- }: Props,
63
- ref: any,
64
- ) => {
65
- const [displayText, setDisplayText] = useState('');
41
+ const FilePicker = ({
42
+ ref,
43
+ type,
44
+ multiple,
45
+ transitionStyle,
46
+ mode,
47
+ presentationStyle,
48
+ value: valueProp,
49
+ defaultValue,
50
+ onChange,
51
+ onCancel,
52
+ onError,
53
+ children,
54
+ ...rest
55
+ }: Props) => {
56
+ const [value, onValueChange] = useControlledValue<
57
+ DocumentResult | DocumentResult[] | undefined
58
+ >({
59
+ value: valueProp,
60
+ defaultValue,
61
+ onChange,
62
+ });
66
63
 
67
64
  const { openFilePicker } = useFilePicker({
68
65
  type,
@@ -74,60 +71,34 @@ const FilePicker = (
74
71
  onError,
75
72
  });
76
73
 
77
- const onSetInputValue = useCallback(
78
- (response: DocumentResult | DocumentResult[] | undefined) => {
79
- if (Array.isArray(response)) {
80
- if (response.length > 1) {
81
- setDisplayText(`${response.length} file${response.length > 1 ? 's' : ''}`);
82
- return;
83
- }
74
+ const displayText = useMemo(() => {
75
+ if (!value) return '';
84
76
 
85
- setDisplayText(response[0].name || '');
86
- return;
77
+ if (Array.isArray(value)) {
78
+ if (value.length > 1) {
79
+ return `${value.length} files`;
87
80
  }
81
+ return value[0]?.name || '';
82
+ }
88
83
 
89
- setDisplayText(response?.name || '');
90
- },
91
- [],
92
- );
84
+ return value.name || '';
85
+ }, [value]);
93
86
 
94
87
  const onPress = useCallback(() => {
95
88
  openFilePicker(response => {
96
- onSetInputValue(response);
97
-
98
- onChange?.(response);
89
+ onValueChange(response);
99
90
  });
100
- }, [onChange, onSetInputValue, openFilePicker]);
101
-
102
- const rightElement = useMemo(() => {
103
- if (!loading) {
104
- return (
105
- <>
106
- {rightProp || (
107
- <IconButton type="material-community" name="upload" onPress={onPress} />
108
- )}
109
- </>
110
- );
111
- } else {
112
- return <>{progressIndicator || <ActivityIndicator />}</>;
113
- }
114
- }, [loading, onPress, progressIndicator, rightProp]);
115
-
116
- // if the value changes, we only want file name or the length of the array to display the text
117
- useEffect(() => {
118
- onSetInputValue(value);
119
- }, [onSetInputValue, value]);
91
+ }, [onValueChange, openFilePicker]);
120
92
 
121
93
  return (
122
- <TextInput
123
- label="Choose file"
124
- {...rest}
125
- value={displayText}
126
- editable={false}
127
- right={rightElement}
128
- ref={ref}
129
- />
94
+ <TextInput value={displayText} {...rest} editable={false} ref={ref}>
95
+ <TextInput.Label>Choose file</TextInput.Label>
96
+ <TextInput.Right>
97
+ <IconButton type="material-community" name="upload" onPress={onPress} />
98
+ </TextInput.Right>
99
+ {children}
100
+ </TextInput>
130
101
  );
131
102
  };
132
103
 
133
- export default memo(forwardRef(FilePicker));
104
+ export default memo(FilePicker);
@@ -24,41 +24,6 @@ export type Props = TextProps & {
24
24
  testID?: string;
25
25
  };
26
26
 
27
- /**
28
- * Helper text is used in conjuction with input elements to provide additional hints for the user.
29
- *
30
- * <div class="screenshots">
31
- * <img class="small" src="screenshots/helper-text.gif" />
32
- * </div>
33
- *
34
- * ## Usage
35
- * ```js
36
- * import * as React from 'react';
37
- * import { View } from 'react-native';
38
- * import { HelperText, TextInput } from 'react-native-paper';
39
- *
40
- * const MyComponent = () => {
41
- * const [text, setText] = React.useState('');
42
- *
43
- * const onChangeText = text => setText(text);
44
- *
45
- * const hasErrors = () => {
46
- * return !text.includes('@');
47
- * };
48
- *
49
- * return (
50
- * <View>
51
- * <TextInput label="Email" value={text} onChangeText={onChangeText} />
52
- * <HelperText variant="error" visible={hasErrors()}>
53
- * Email address is invalid!
54
- * </HelperText>
55
- * </View>
56
- * );
57
- * };
58
- *
59
- * export default MyComponent;
60
- * ```
61
- */
62
27
  const HelperText = ({
63
28
  style: styleProp,
64
29
  variant = 'info',