react95-native-rabbl 0.1.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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +24 -0
  3. package/package.json +154 -0
  4. package/src/assets/fonts/src/ms-sans-serif/MS Sans Serif.ttf +0 -0
  5. package/src/assets/fonts/src/ms-sans-serif/license.txt +4 -0
  6. package/src/assets/fonts/src/ms-sans-serif/readme.txt +26 -0
  7. package/src/assets/fonts/src/ms-sans-serif-bold/MS Sans Serif Bold.ttf +0 -0
  8. package/src/assets/fonts/src/ms-sans-serif-bold/license.txt +4 -0
  9. package/src/assets/fonts/src/ms-sans-serif-bold/readme.txt +26 -0
  10. package/src/components/AppBar/AppBar.spec.tsx +140 -0
  11. package/src/components/AppBar/AppBar.tsx +43 -0
  12. package/src/components/AppBar/AppBarBackAction.tsx +20 -0
  13. package/src/components/AppBar/AppBarContent.tsx +84 -0
  14. package/src/components/AppBar/index.ts +1 -0
  15. package/src/components/Button/Button.spec.tsx +59 -0
  16. package/src/components/Button/Button.tsx +236 -0
  17. package/src/components/Button/index.ts +1 -0
  18. package/src/components/Card/Card.spec.tsx +54 -0
  19. package/src/components/Card/Card.tsx +88 -0
  20. package/src/components/Card/CardContent.tsx +23 -0
  21. package/src/components/Card/index.ts +1 -0
  22. package/src/components/Checkbox/Checkbox.tsx +10 -0
  23. package/src/components/Checkbox/index.ts +1 -0
  24. package/src/components/ColorButton/ColorButton.tsx +69 -0
  25. package/src/components/ColorButton/index.ts +1 -0
  26. package/src/components/ColorPicker/ColorPicker.tsx +109 -0
  27. package/src/components/ColorPicker/index.ts +1 -0
  28. package/src/components/Desktop/Desktop.spec.tsx +32 -0
  29. package/src/components/Desktop/Desktop.tsx +132 -0
  30. package/src/components/Desktop/index.tsx +1 -0
  31. package/src/components/Divider/Divider.spec.tsx +47 -0
  32. package/src/components/Divider/Divider.tsx +52 -0
  33. package/src/components/Divider/index.tsx +1 -0
  34. package/src/components/FAB/FAB.tsx +288 -0
  35. package/src/components/FAB/FABGroup.tsx +385 -0
  36. package/src/components/FAB/index.ts +1 -0
  37. package/src/components/Fieldset/Fieldset.spec.tsx +48 -0
  38. package/src/components/Fieldset/Fieldset.tsx +107 -0
  39. package/src/components/Fieldset/index.ts +1 -0
  40. package/src/components/Hourglass/Hourglass.spec.tsx +24 -0
  41. package/src/components/Hourglass/Hourglass.tsx +43 -0
  42. package/src/components/Hourglass/base64hourglass.ts +3 -0
  43. package/src/components/Hourglass/index.ts +1 -0
  44. package/src/components/Icons/ArrowIcon.tsx +85 -0
  45. package/src/components/Icons/CheckmarkIcon.tsx +55 -0
  46. package/src/components/Icons/ChevronIcon.tsx +93 -0
  47. package/src/components/Icons/CloseIcon.tsx +48 -0
  48. package/src/components/Icons/index.ts +4 -0
  49. package/src/components/Label/Label.tsx +77 -0
  50. package/src/components/Label/index.ts +1 -0
  51. package/src/components/List/List.tsx +3 -0
  52. package/src/components/List/ListAccordion.tsx +154 -0
  53. package/src/components/List/ListItem.tsx +74 -0
  54. package/src/components/List/ListSection.tsx +51 -0
  55. package/src/components/List/index.ts +3 -0
  56. package/src/components/Menu/Menu.tsx +100 -0
  57. package/src/components/Menu/MenuItem.tsx +100 -0
  58. package/src/components/Menu/index.ts +1 -0
  59. package/src/components/NumberInput/NumberInput.spec.tsx +119 -0
  60. package/src/components/NumberInput/NumberInput.tsx +144 -0
  61. package/src/components/NumberInput/index.ts +1 -0
  62. package/src/components/Panel/Panel.spec.tsx +29 -0
  63. package/src/components/Panel/Panel.tsx +75 -0
  64. package/src/components/Panel/index.ts +1 -0
  65. package/src/components/Portal/Portal.tsx +52 -0
  66. package/src/components/Portal/PortalConsumer.tsx +48 -0
  67. package/src/components/Portal/PortalHost.tsx +150 -0
  68. package/src/components/Portal/PortalManager.tsx +57 -0
  69. package/src/components/Portal/index.ts +1 -0
  70. package/src/components/Progress/Progress.tsx +125 -0
  71. package/src/components/Progress/index.ts +1 -0
  72. package/src/components/Radio/Radio.tsx +14 -0
  73. package/src/components/Radio/index.ts +1 -0
  74. package/src/components/ScrollPanel/ScrollPanel.tsx +72 -0
  75. package/src/components/ScrollPanel/index.ts +1 -0
  76. package/src/components/ScrollView/ScrollView.tsx +284 -0
  77. package/src/components/ScrollView/index.ts +1 -0
  78. package/src/components/Select/Select.tsx +229 -0
  79. package/src/components/Select/SelectBase.tsx +119 -0
  80. package/src/components/Select/SelectBox.tsx +66 -0
  81. package/src/components/Select/index.ts +2 -0
  82. package/src/components/Slider/Slider.tsx +301 -0
  83. package/src/components/Slider/index.ts +1 -0
  84. package/src/components/Snackbar/Snackbar.tsx +260 -0
  85. package/src/components/Snackbar/SnackbarContent.tsx +23 -0
  86. package/src/components/Snackbar/index.ts +1 -0
  87. package/src/components/SwitchBase/SwitchBase.tsx +193 -0
  88. package/src/components/SwitchBase/index.ts +1 -0
  89. package/src/components/Tabs/Tabs.tsx +208 -0
  90. package/src/components/Tabs/index.ts +1 -0
  91. package/src/components/TextInput/TextInput.tsx +82 -0
  92. package/src/components/TextInput/index.ts +1 -0
  93. package/src/components/Toolbar/Toolbar.tsx +113 -0
  94. package/src/components/Toolbar/index.ts +1 -0
  95. package/src/components/Typography/Anchor.tsx +38 -0
  96. package/src/components/Typography/Text.spec.tsx +30 -0
  97. package/src/components/Typography/Text.tsx +55 -0
  98. package/src/components/Typography/Title.tsx +58 -0
  99. package/src/components/Typography/index.ts +3 -0
  100. package/src/components/Window/Window.tsx +132 -0
  101. package/src/components/Window/index.ts +1 -0
  102. package/src/core/Provider.tsx +52 -0
  103. package/src/core/theming.tsx +8 -0
  104. package/src/hooks/useAsyncReference.ts +22 -0
  105. package/src/hooks/useControlledOrUncontrolled.ts +23 -0
  106. package/src/index.ts +38 -0
  107. package/src/styles/shadow.tsx +36 -0
  108. package/src/styles/styleElements.tsx +105 -0
  109. package/src/styles/styles.ts +129 -0
  110. package/src/styles/themes/aiee.ts +36 -0
  111. package/src/styles/themes/ash.ts +35 -0
  112. package/src/styles/themes/azureOrange.ts +33 -0
  113. package/src/styles/themes/bee.ts +33 -0
  114. package/src/styles/themes/blackAndWhite.ts +33 -0
  115. package/src/styles/themes/blue.ts +36 -0
  116. package/src/styles/themes/brick.ts +33 -0
  117. package/src/styles/themes/candy.ts +33 -0
  118. package/src/styles/themes/cherry.ts +36 -0
  119. package/src/styles/themes/coldGray.ts +34 -0
  120. package/src/styles/themes/counterStrike.ts +33 -0
  121. package/src/styles/themes/darkTeal.ts +36 -0
  122. package/src/styles/themes/eggplant.ts +33 -0
  123. package/src/styles/themes/fxDev.ts +36 -0
  124. package/src/styles/themes/highContrast.ts +33 -0
  125. package/src/styles/themes/hotChocolate.ts +36 -0
  126. package/src/styles/themes/index.ts +103 -0
  127. package/src/styles/themes/lilac.ts +33 -0
  128. package/src/styles/themes/lilacRoseDark.ts +34 -0
  129. package/src/styles/themes/maple.ts +33 -0
  130. package/src/styles/themes/marine.ts +33 -0
  131. package/src/styles/themes/matrix.ts +33 -0
  132. package/src/styles/themes/millenium.ts +33 -0
  133. package/src/styles/themes/modernDark.ts +33 -0
  134. package/src/styles/themes/molecule.ts +33 -0
  135. package/src/styles/themes/monochrome.ts +0 -0
  136. package/src/styles/themes/ninjaTurtles.ts +33 -0
  137. package/src/styles/themes/olive.ts +33 -0
  138. package/src/styles/themes/original.ts +33 -0
  139. package/src/styles/themes/pamelaAnderson.ts +33 -0
  140. package/src/styles/themes/plum.ts +33 -0
  141. package/src/styles/themes/polarized.ts +36 -0
  142. package/src/styles/themes/powerShell.ts +36 -0
  143. package/src/styles/themes/rainyDay.ts +33 -0
  144. package/src/styles/themes/raspberry.ts +36 -0
  145. package/src/styles/themes/redWine.ts +36 -0
  146. package/src/styles/themes/rose.ts +33 -0
  147. package/src/styles/themes/seawater.ts +36 -0
  148. package/src/styles/themes/slate.ts +33 -0
  149. package/src/styles/themes/solarizedDark.ts +36 -0
  150. package/src/styles/themes/solarizedLight.ts +36 -0
  151. package/src/styles/themes/spruce.ts +33 -0
  152. package/src/styles/themes/stormClouds.ts +36 -0
  153. package/src/styles/themes/theSixtiesUSA.ts +33 -0
  154. package/src/styles/themes/tokyoDark.ts +33 -0
  155. package/src/styles/themes/tooSexy.ts +33 -0
  156. package/src/styles/themes/travel.ts +33 -0
  157. package/src/styles/themes/vaporTeal.ts +33 -0
  158. package/src/styles/themes/vermillion.ts +33 -0
  159. package/src/styles/themes/violetDark.ts +33 -0
  160. package/src/styles/themes/water.ts +33 -0
  161. package/src/styles/themes/wmii.ts +36 -0
  162. package/src/types.tsx +55 -0
  163. package/src/utils/index.ts +57 -0
@@ -0,0 +1,100 @@
1
+ import React from 'react';
2
+ import { StyleProp, StyleSheet, ViewStyle, View } from 'react-native';
3
+ import type { LayoutChangeEvent } from 'react-native';
4
+
5
+ import type { Theme, Orientation } from '../../types';
6
+ import { withTheme } from '../../core/theming';
7
+
8
+ import { Panel } from '../..';
9
+
10
+ import MenuItem from './MenuItem';
11
+
12
+ type Props = React.ComponentPropsWithRef<typeof View> & {
13
+ anchor?: React.ReactNode;
14
+ children: React.ReactNode;
15
+ open?: boolean;
16
+ orientation?: Orientation;
17
+ style?: StyleProp<ViewStyle>;
18
+ theme: Theme;
19
+ horizontalAlignment?: 'left' | 'right';
20
+ verticalAlignment?: 'above' | 'below';
21
+ };
22
+
23
+ const Menu = ({
24
+ anchor,
25
+ children,
26
+ open = false,
27
+ orientation = 'vertical',
28
+ style = {},
29
+ theme,
30
+ horizontalAlignment: horizontalAlign = 'left',
31
+ verticalAlignment: verticalAlign = 'below',
32
+ ...rest
33
+ }: Props) => {
34
+ const [menuSize, setMenuSize] = React.useState({ width: 0, height: 0 });
35
+
36
+ const handleMenuLayout = (e: LayoutChangeEvent) => {
37
+ const { width, height } = e.nativeEvent.layout;
38
+ setMenuSize({ width, height });
39
+ };
40
+
41
+ const menuPosition: StyleProp<ViewStyle> = {};
42
+ if (verticalAlign === 'below') {
43
+ menuPosition.top = '100%';
44
+ } else {
45
+ menuPosition.top = -menuSize.height;
46
+ }
47
+ if (horizontalAlign === 'left') {
48
+ menuPosition.left = 0;
49
+ } else {
50
+ menuPosition.right = menuSize.width;
51
+ }
52
+
53
+ return (
54
+ <View style={styles.wrapper}>
55
+ {anchor}
56
+
57
+ {open && (
58
+ <View style={[styles.menuWrapper, menuPosition]}>
59
+ <Panel
60
+ {...rest}
61
+ theme={theme}
62
+ variant='raised'
63
+ elevation={2}
64
+ onLayout={handleMenuLayout}
65
+ style={[
66
+ styles.menu,
67
+ {
68
+ display: 'flex',
69
+ flexDirection: orientation === 'vertical' ? 'column' : 'row',
70
+ },
71
+ style,
72
+ ]}
73
+ >
74
+ {children}
75
+ </Panel>
76
+ </View>
77
+ )}
78
+ </View>
79
+ );
80
+ };
81
+
82
+ const styles = StyleSheet.create({
83
+ wrapper: {
84
+ position: 'relative',
85
+ },
86
+ menuWrapper: {
87
+ position: 'absolute',
88
+ },
89
+ menu: {
90
+ width: 'auto',
91
+ position: 'absolute',
92
+ height: 'auto',
93
+ flexGrow: 0,
94
+ padding: 6,
95
+ },
96
+ });
97
+
98
+ Menu.Item = MenuItem;
99
+
100
+ export default withTheme(Menu);
@@ -0,0 +1,100 @@
1
+ import React, { useState } from 'react';
2
+ import {
3
+ StyleSheet,
4
+ View,
5
+ TouchableHighlight,
6
+ StyleProp,
7
+ ViewStyle,
8
+ } from 'react-native';
9
+
10
+ import type { Theme, Sizes } from '../../types';
11
+ import { withTheme } from '../../core/theming';
12
+ import { blockSizes, builtTextStyles } from '../../styles/styles';
13
+
14
+ import { Text } from '../..';
15
+
16
+ // TODO: add icon prop
17
+
18
+ type Props = {
19
+ disabled?: boolean;
20
+ onPress: () => void;
21
+ primary?: boolean;
22
+ size?: Sizes;
23
+ style?: StyleProp<ViewStyle>;
24
+ theme: Theme;
25
+ title: string;
26
+ };
27
+
28
+ export const Item = ({
29
+ disabled,
30
+ onPress,
31
+ primary = false,
32
+ size = 'md',
33
+ style,
34
+ theme,
35
+ title,
36
+ ...rest
37
+ }: Props) => {
38
+ const [isPressed, setIsPressed] = useState(false);
39
+
40
+ const textStyles = builtTextStyles(theme);
41
+ return (
42
+ <View
43
+ {...rest}
44
+ style={[
45
+ styles.item,
46
+ { height: blockSizes[size] },
47
+ {
48
+ backgroundColor: isPressed ? theme.hoverBackground : theme.material,
49
+ },
50
+ style,
51
+ ]}
52
+ >
53
+ <TouchableHighlight
54
+ style={[styles.button]}
55
+ onPress={onPress}
56
+ disabled={disabled}
57
+ onHideUnderlay={() => setIsPressed(false)}
58
+ onShowUnderlay={() => setIsPressed(true)}
59
+ underlayColor='none'
60
+ // TODO: which accessibilityRole put in here?
61
+ accessibilityRole='menuitem'
62
+ accessibilityState={{ disabled }}
63
+ >
64
+ <View pointerEvents='none' style={[styles.content]}>
65
+ <Text
66
+ theme={theme}
67
+ bold={primary}
68
+ style={[
69
+ disabled ? textStyles.disabled : textStyles.default,
70
+ !disabled && {
71
+ color: isPressed
72
+ ? theme.materialTextInvert
73
+ : theme.materialText,
74
+ },
75
+ ]}
76
+ >
77
+ {title}
78
+ </Text>
79
+ </View>
80
+ </TouchableHighlight>
81
+ </View>
82
+ );
83
+ };
84
+
85
+ const styles = StyleSheet.create({
86
+ item: {
87
+ position: 'relative',
88
+ },
89
+ button: {
90
+ flex: 1,
91
+ justifyContent: 'center',
92
+ alignItems: 'center',
93
+ paddingHorizontal: 8,
94
+ },
95
+ content: {
96
+ alignSelf: 'flex-start',
97
+ },
98
+ });
99
+
100
+ export default withTheme(Item);
@@ -0,0 +1 @@
1
+ export { default } from './Menu';
@@ -0,0 +1,119 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@testing-library/react-native';
3
+ import { NumberInput } from '../..';
4
+
5
+ describe('<NumberInput />', () => {
6
+ it('should call onChange on increment press', () => {
7
+ const handleChange = jest.fn();
8
+
9
+ const { getByTestId } = render(
10
+ <NumberInput onChange={handleChange} defaultValue={2} />,
11
+ );
12
+ const spinButton = getByTestId('increment');
13
+ fireEvent(spinButton, 'press');
14
+ expect(handleChange).toHaveBeenCalledTimes(1);
15
+ expect(handleChange).toHaveBeenCalledWith(3);
16
+ });
17
+
18
+ it('should call onChange on decrement press', () => {
19
+ const handleChange = jest.fn();
20
+
21
+ const { getByTestId } = render(
22
+ <NumberInput onChange={handleChange} defaultValue={2} />,
23
+ );
24
+ const spinButton = getByTestId('decrement');
25
+ fireEvent(spinButton, 'press');
26
+ expect(handleChange).toHaveBeenCalledTimes(1);
27
+ expect(handleChange).toHaveBeenCalledWith(1);
28
+ });
29
+
30
+ it('should reach max value', () => {
31
+ const { getByTestId } = render(
32
+ <NumberInput defaultValue={90} min={0} max={100} step={10} />,
33
+ );
34
+ const input = getByTestId('input');
35
+ const incrementButton = getByTestId('increment');
36
+ fireEvent(incrementButton, 'press');
37
+
38
+ expect(input.props.value).toBe('100');
39
+ });
40
+
41
+ it('should reach min value', () => {
42
+ const { getByTestId } = render(
43
+ <NumberInput defaultValue={10} min={0} max={100} step={10} />,
44
+ );
45
+ const input = getByTestId('input');
46
+ const decrementButton = getByTestId('decrement');
47
+ fireEvent(decrementButton, 'press');
48
+
49
+ expect(input.props.value).toBe('0');
50
+ });
51
+
52
+ describe('prop: step', () => {
53
+ it('should be 1 by default', () => {
54
+ const { getByTestId } = render(<NumberInput defaultValue={0} />);
55
+ const input = getByTestId('input');
56
+
57
+ const incrementButton = getByTestId('increment');
58
+ fireEvent(incrementButton, 'press');
59
+
60
+ expect(input.props.value).toBe('1');
61
+ });
62
+
63
+ it('should change value by specified step', () => {
64
+ const { getByTestId } = render(
65
+ <NumberInput defaultValue={10} step={3} />,
66
+ );
67
+ const input = getByTestId('input');
68
+
69
+ const decrementButton = getByTestId('decrement');
70
+ fireEvent(decrementButton, 'press');
71
+
72
+ expect(input.props.value).toBe('7');
73
+ });
74
+
75
+ it('should handle decimal step', () => {
76
+ const { getByTestId } = render(
77
+ <NumberInput defaultValue={10} step={0.3} />,
78
+ );
79
+ const input = getByTestId('input');
80
+
81
+ const decrementButton = getByTestId('decrement');
82
+ fireEvent(decrementButton, 'press');
83
+
84
+ expect(input.props.value).toBe('9.7');
85
+ });
86
+ });
87
+
88
+ describe('prop: disabled', () => {
89
+ // it('should render disabled', () => {
90
+ // const { getByTestId } = render(
91
+ // <NumberInput defaultValue={10} disabled />,
92
+ // );
93
+ // const input = getByTestId('input');
94
+ // const incrementButton = getByTestId('increment');
95
+ // const decrementButton = getByTestId('decrement');
96
+ // expect(input.props.editable).toBe(false);
97
+ // expect(incrementButton.props.disabled).toBe(true);
98
+ // expect(decrementButton.props.disabled).toBe(true);
99
+ // });
100
+ // it('should not react to button clicks', () => {
101
+ // const { getByTestId } = render(
102
+ // <NumberInput defaultValue={10} disabled />,
103
+ // );
104
+ // const input = getByTestId('input');
105
+ // const incrementButton = getByTestId('increment');
106
+ // const decrementButton = getByTestId('decrement');
107
+ // fireEvent(incrementButton, 'press');
108
+ // expect(input.props.value).toBe('10');
109
+ // fireEvent(decrementButton, 'press');
110
+ // expect(input.props.value).toBe('10');
111
+ // });
112
+ });
113
+
114
+ describe('prop: width', () => {
115
+ it('should render component of specified width', () => {});
116
+
117
+ it('should handle %', () => {});
118
+ });
119
+ });
@@ -0,0 +1,144 @@
1
+ import React from 'react';
2
+ import { View, StyleProp, StyleSheet, ViewStyle } from 'react-native';
3
+ import useControlledOrUncontrolled from '../../hooks/useControlledOrUncontrolled';
4
+
5
+ import type { Theme, DimensionValue } from '../../types';
6
+ import { withTheme } from '../../core/theming';
7
+
8
+ import { blockSizes } from '../../styles/styles';
9
+ import { clamp } from '../../utils';
10
+
11
+ import { TextInput, Button, ArrowIcon } from '../..';
12
+
13
+ type Props = {
14
+ defaultValue?: number;
15
+ disabled?: boolean;
16
+ inputWidth?: DimensionValue;
17
+ max?: number | null;
18
+ min?: number | null;
19
+ onChange?: (value: number) => void;
20
+ step?: number;
21
+ style?: StyleProp<ViewStyle>;
22
+ theme: Theme;
23
+ value?: number;
24
+ variant?: 'default' | 'flat';
25
+ };
26
+
27
+ // TODO: allow to center input text horizontally
28
+ // TODO: how are uncontrolled inputs handled in RN?
29
+ const NumberInput = ({
30
+ defaultValue,
31
+ disabled,
32
+ inputWidth,
33
+ max = null,
34
+ min = null,
35
+ onChange,
36
+ step = 1,
37
+ style = {},
38
+ theme,
39
+ value,
40
+ variant = 'default',
41
+ ...rest
42
+ }: Props) => {
43
+ const [valueDerived, setValueState] = useControlledOrUncontrolled({
44
+ value,
45
+ defaultValue,
46
+ });
47
+
48
+ const handleClick = (val: number) => {
49
+ const stateValue = parseFloat(valueDerived);
50
+ const newValue = clamp(
51
+ +parseFloat((stateValue + val).toString()).toFixed(2),
52
+ min,
53
+ max,
54
+ ).toString();
55
+
56
+ setValueState(parseFloat(newValue));
57
+
58
+ if (onChange) {
59
+ onChange(parseFloat(newValue));
60
+ }
61
+ };
62
+
63
+ const valueDerivedNumber = parseFloat(valueDerived);
64
+
65
+ const isDecrementDisabled = disabled || valueDerivedNumber === min;
66
+ const isIncrementDisabled = disabled || valueDerivedNumber === max;
67
+ const isFlat = variant === 'flat';
68
+
69
+ return (
70
+ <View
71
+ style={[styles.wrapper, style]}
72
+ accessibilityState={{ disabled }}
73
+ // TODO: are these accessibility traits correct?
74
+ accessibilityRole='adjustable'
75
+ accessibilityValue={{
76
+ min: min === null ? undefined : min,
77
+ max: max === null ? undefined : max,
78
+ now: valueDerived,
79
+ }}
80
+ >
81
+ <Button
82
+ theme={theme}
83
+ disabled={isDecrementDisabled}
84
+ onPress={() => handleClick(-step)}
85
+ variant={isFlat ? 'flat' : 'raised'}
86
+ style={styles.button}
87
+ testID='decrement'
88
+ >
89
+ <ArrowIcon
90
+ theme={theme}
91
+ segments={4}
92
+ disabled={isDecrementDisabled}
93
+ direction='left'
94
+ />
95
+ </Button>
96
+ <TextInput
97
+ theme={theme}
98
+ variant={variant}
99
+ disabled={disabled}
100
+ value={valueDerived.toString()}
101
+ style={[styles.input, { width: inputWidth || 'auto' }]}
102
+ editable={false}
103
+ // eslint-disable-next-line react/jsx-props-no-spreading
104
+ testID='input'
105
+ {...rest}
106
+ />
107
+ <Button
108
+ theme={theme}
109
+ disabled={isIncrementDisabled}
110
+ onPress={() => handleClick(step)}
111
+ variant={isFlat ? 'flat' : 'raised'}
112
+ style={styles.button}
113
+ testID='increment'
114
+ >
115
+ <ArrowIcon
116
+ theme={theme}
117
+ segments={4}
118
+ disabled={isIncrementDisabled}
119
+ direction='right'
120
+ />
121
+ </Button>
122
+ </View>
123
+ );
124
+ };
125
+
126
+ const styles = StyleSheet.create({
127
+ wrapper: {
128
+ display: 'flex',
129
+ flexDirection: 'row',
130
+ justifyContent: 'center',
131
+ },
132
+ input: {
133
+ marginHorizontal: 2,
134
+ minWidth: blockSizes.md + 2,
135
+ },
136
+ button: {
137
+ width: blockSizes.md,
138
+ },
139
+ buttonText: {
140
+ fontSize: 24,
141
+ },
142
+ });
143
+
144
+ export default withTheme(NumberInput);
@@ -0,0 +1 @@
1
+ export { default } from './NumberInput';
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react-native';
3
+
4
+ import { testId } from './Panel';
5
+ import { Panel, Text } from '../..';
6
+
7
+ describe('<Panel />', () => {
8
+ it('should render children', () => {
9
+ const { getByTestId } = render(
10
+ <Panel>
11
+ <Text>Banana dance</Text>
12
+ </Panel>,
13
+ );
14
+
15
+ expect(getByTestId(testId)).toHaveTextContent('Banana dance');
16
+ });
17
+
18
+ it('should render custom styles', () => {
19
+ const style = { backgroundColor: 'teal' };
20
+
21
+ const { getByTestId } = render(
22
+ <Panel style={style}>
23
+ <Text>Panel</Text>
24
+ </Panel>,
25
+ );
26
+
27
+ expect(getByTestId(testId)).toHaveStyle(style);
28
+ });
29
+ });
@@ -0,0 +1,75 @@
1
+ import React from 'react';
2
+ import { StyleProp, StyleSheet, Animated, View, ViewStyle } from 'react-native';
3
+
4
+ import { withTheme } from '../../core/theming';
5
+ import { Border } from '../../styles/styleElements';
6
+ import shadow from '../../styles/shadow';
7
+ import type { Theme } from '../../types';
8
+
9
+ export const testId = 'panel';
10
+
11
+ // TODO: common interface with styleElements/Border ?
12
+ type Props = React.ComponentPropsWithRef<typeof View> & {
13
+ background?: 'material' | 'canvas' | 'materialDark';
14
+ children?: React.ReactNode;
15
+ elevation?: number;
16
+ invert?: boolean;
17
+ radius?: number;
18
+ style?: StyleProp<ViewStyle>;
19
+ theme: Theme;
20
+ variant?: 'default' | 'well' | 'raised' | 'clear' | 'cutout';
21
+ };
22
+
23
+ const Panel = ({
24
+ background = 'material',
25
+ children,
26
+ elevation = 0,
27
+ invert = false,
28
+ radius = 0,
29
+ style = {},
30
+ theme,
31
+ variant = 'default',
32
+ ...rest
33
+ }: Props) => {
34
+ const getBackgroundColor = () => {
35
+ return theme[background];
36
+ };
37
+
38
+ return (
39
+ // TODO: fix this TS error
40
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
41
+ // @ts-ignore
42
+ <Animated.View
43
+ {...rest}
44
+ style={[
45
+ styles.container,
46
+ {
47
+ padding: variant === 'well' ? 2 : 4,
48
+ backgroundColor: getBackgroundColor(),
49
+ borderRadius: radius,
50
+ },
51
+ shadow(elevation),
52
+ style,
53
+ ]}
54
+ testID={testId}
55
+ >
56
+ {variant !== 'clear' && (
57
+ <Border
58
+ theme={theme}
59
+ variant={variant}
60
+ radius={radius}
61
+ invert={invert}
62
+ />
63
+ )}
64
+ {children}
65
+ </Animated.View>
66
+ );
67
+ };
68
+
69
+ const styles = StyleSheet.create({
70
+ container: {
71
+ position: 'relative',
72
+ },
73
+ });
74
+
75
+ export default withTheme(Panel);
@@ -0,0 +1 @@
1
+ export { default } from './Panel';
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import PortalConsumer from './PortalConsumer';
3
+ import PortalHost, { PortalContext, PortalMethods } from './PortalHost';
4
+
5
+ import type { Theme } from '../../types';
6
+ import { ThemeProvider, withTheme } from '../../core/theming';
7
+
8
+ type Props = {
9
+ /**
10
+ * Content of the `Portal`.
11
+ */
12
+ children: React.ReactNode;
13
+ /**
14
+ * @optional
15
+ */
16
+ theme: Theme;
17
+ };
18
+
19
+ /**
20
+ * Portal allows to render a component at a different place in the parent tree.
21
+ * You can use it to render content which should appear above other elements, similar to `Modal`.
22
+ * It requires a [`Portal.Host`](portal-host.html) component to be rendered somewhere in the parent tree.
23
+ *
24
+ * ## Usage
25
+ * ```js
26
+ * import * as React from 'react';
27
+ * import { Portal, Text } from 'react-native-paper';
28
+ *
29
+ * const MyComponent = () => (
30
+ * <Portal>
31
+ * <Text>This is rendered at a different place</Text>
32
+ * </Portal>
33
+ * );
34
+ *
35
+ * export default MyComponent;
36
+ * ```
37
+ */
38
+ const Portal = ({ children, theme }: Props) => {
39
+ return (
40
+ <PortalContext.Consumer>
41
+ {manager => (
42
+ <PortalConsumer manager={manager as PortalMethods}>
43
+ <ThemeProvider theme={theme}>{children}</ThemeProvider>
44
+ </PortalConsumer>
45
+ )}
46
+ </PortalContext.Consumer>
47
+ );
48
+ };
49
+
50
+ Portal.Host = PortalHost;
51
+
52
+ export default withTheme(Portal);
@@ -0,0 +1,48 @@
1
+ /* eslint-disable react/destructuring-assignment */
2
+ import * as React from 'react';
3
+ import type { PortalMethods } from './PortalHost';
4
+
5
+ type Props = {
6
+ manager: PortalMethods;
7
+ children: React.ReactNode;
8
+ };
9
+
10
+ export default class PortalConsumer extends React.Component<Props> {
11
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
+ private key: any;
13
+
14
+ async componentDidMount() {
15
+ this.checkManager();
16
+
17
+ // Delay updating to prevent React from going to infinite loop
18
+ await Promise.resolve();
19
+
20
+ this.key = this.props.manager.mount(this.props.children);
21
+ }
22
+
23
+ componentDidUpdate() {
24
+ this.checkManager();
25
+
26
+ this.props.manager.update(this.key, this.props.children);
27
+ }
28
+
29
+ componentWillUnmount() {
30
+ this.checkManager();
31
+
32
+ this.props.manager.unmount(this.key);
33
+ }
34
+
35
+ private checkManager() {
36
+ if (!this.props.manager) {
37
+ throw new Error(
38
+ 'Looks like you forgot to wrap your root component with `Provider` component from `react-native-paper`.\n\n' +
39
+ "Please read our getting-started guide and make sure you've followed all the required steps.\n\n" +
40
+ 'https://callstack.github.io/react-native-paper/getting-started.html',
41
+ );
42
+ }
43
+ }
44
+
45
+ render() {
46
+ return null;
47
+ }
48
+ }