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,236 @@
1
+ import React, { useState } from 'react';
2
+ import {
3
+ StyleSheet,
4
+ // TODO: use Pressable instead of TouchableHighlight?
5
+ TouchableHighlight,
6
+ View,
7
+ ImageBackground,
8
+ StyleProp,
9
+ ViewStyle,
10
+ } from 'react-native';
11
+ import type { Sizes, Theme } from '../../types';
12
+ import { withTheme } from '../../core/theming';
13
+ import { blockSizes, buildBorderStyles } from '../../styles/styles';
14
+
15
+ import { Text } from '../..';
16
+
17
+ export const testId = 'button';
18
+
19
+ export type ButtonVariants = 'menu' | 'flat' | 'default' | 'raised';
20
+
21
+ type ButtonProps = React.ComponentPropsWithRef<typeof View> & {
22
+ accessibilityLabel?: string;
23
+ active?: boolean;
24
+ children: React.ReactNode;
25
+ disabled?: boolean;
26
+ onPress?: () => void;
27
+ onLongPress?: () => void;
28
+ primary?: boolean;
29
+ size?: Sizes;
30
+ square?: boolean;
31
+ style?: StyleProp<ViewStyle>;
32
+ variant?: ButtonVariants;
33
+ theme: Theme;
34
+ };
35
+
36
+ const Button = ({
37
+ accessible,
38
+ accessibilityLabel,
39
+ active = false,
40
+ children,
41
+ disabled = false,
42
+ onPress,
43
+ onLongPress,
44
+ primary = false,
45
+ size = 'md',
46
+ square = false,
47
+ style = {},
48
+ variant = 'default',
49
+ theme,
50
+ ...rest
51
+ }: ButtonProps) => {
52
+ const [isPressed, setIsPressed] = useState(false);
53
+
54
+ const getWidth = () => {
55
+ return square ? blockSizes[size] : 'auto';
56
+ };
57
+
58
+ const isFlat = variant === 'flat';
59
+
60
+ const getBackgroundColor = () => {
61
+ if (isFlat) {
62
+ return disabled ? theme.flatLight : 'transparent';
63
+ }
64
+ return theme.material;
65
+ };
66
+
67
+ return (
68
+ <View
69
+ style={[
70
+ styles.wrapper,
71
+ { height: blockSizes[size], width: getWidth() },
72
+ style,
73
+ ]}
74
+ testID={testId}
75
+ {...rest}
76
+ >
77
+ <Borders
78
+ theme={theme}
79
+ isPressed={isPressed}
80
+ variant={variant}
81
+ primary={primary}
82
+ active={active}
83
+ style={{
84
+ backgroundColor: getBackgroundColor(),
85
+ }}
86
+ />
87
+
88
+ <TouchableHighlight
89
+ style={[
90
+ styles.content,
91
+ { paddingHorizontal: square ? 0 : 10 },
92
+ { marginTop: active || isPressed ? 2 : 0 },
93
+ ]}
94
+ onPress={onPress}
95
+ onLongPress={onLongPress}
96
+ disabled={disabled}
97
+ // TODO: use onHideUnderlay or onPressIn?
98
+ onHideUnderlay={() => setIsPressed(false)}
99
+ onShowUnderlay={() => setIsPressed(true)}
100
+ underlayColor='none'
101
+ accessibilityLabel={accessibilityLabel}
102
+ accessibilityTraits={disabled ? ['button', 'disabled'] : 'button'}
103
+ accessibilityComponentType='button'
104
+ accessibilityRole='button'
105
+ accessibilityState={{ disabled }}
106
+ accessible={accessible}
107
+ >
108
+ <View pointerEvents='none'>
109
+ {typeof children === 'string' ? (
110
+ <Text disabled={!isFlat && disabled} secondary={isFlat && disabled}>
111
+ {children}
112
+ </Text>
113
+ ) : (
114
+ children
115
+ )}
116
+ </View>
117
+ </TouchableHighlight>
118
+ </View>
119
+ );
120
+ };
121
+
122
+ const styles = StyleSheet.create({
123
+ wrapper: {
124
+ position: 'relative',
125
+ },
126
+
127
+ content: {
128
+ flex: 1,
129
+ justifyContent: 'center',
130
+ alignItems: 'center',
131
+ },
132
+ });
133
+
134
+ export default withTheme(Button);
135
+
136
+ // Borders acts like a pseudo element that
137
+ // will be positioned absolutely in it's parent element
138
+
139
+ type BorderProps = {
140
+ isPressed?: boolean;
141
+ variant?: ButtonVariants;
142
+ primary?: boolean;
143
+ active?: boolean;
144
+ style?: StyleProp<ViewStyle>;
145
+ theme: Theme;
146
+ };
147
+
148
+ // TODO: pass theme as an argument instead of using context ?
149
+ const Borders = ({
150
+ isPressed = false,
151
+ variant = 'default',
152
+ primary = false,
153
+ active = false,
154
+ style = {},
155
+ theme,
156
+ }: BorderProps) => {
157
+ let wrapper: StyleProp<ViewStyle> = [];
158
+ let outer;
159
+ let inner;
160
+ let focus;
161
+
162
+ const borders = buildBorderStyles(theme);
163
+ if (variant === 'default') {
164
+ wrapper = primary ? [borders.outline] : [];
165
+ outer = [borders.defaultOuter];
166
+ inner = [borders.defaultInner];
167
+ focus = isPressed ? [borders.focusOutline] : [];
168
+ } else if (variant === 'raised') {
169
+ wrapper = primary ? [borders.outline] : [];
170
+ outer = [borders.outsideOuter];
171
+ inner = [borders.outsideInner];
172
+ focus = isPressed ? [borders.focusOutline] : [];
173
+ } else if (variant === 'menu' && (active || isPressed)) {
174
+ wrapper = [borders.well];
175
+ } else if (variant === 'flat') {
176
+ wrapper = primary ? [borders.outline] : [];
177
+ outer = [borders.flat];
178
+ inner = isPressed ? [borders.focusOutline] : [];
179
+ }
180
+
181
+ return (
182
+ <View
183
+ style={[
184
+ borderStyles.position,
185
+ active || isPressed ? borderStyles.invert : {},
186
+ ...wrapper,
187
+ style,
188
+ ]}
189
+ >
190
+ {Array.isArray(outer) && (
191
+ <View style={[borderStyles.position, ...outer]}>
192
+ {Array.isArray(inner) && inner.length > 0 && (
193
+ <View style={[borderStyles.position, ...inner]}>
194
+ {Array.isArray(focus) && !active && (
195
+ <View
196
+ style={[
197
+ borderStyles.position,
198
+ { margin: primary ? 0 : 2 },
199
+ ...focus,
200
+ ]}
201
+ />
202
+ )}
203
+ {active && (
204
+ <ImageBackground
205
+ style={[borderStyles.position]}
206
+ imageStyle={{
207
+ resizeMode: 'repeat',
208
+ }}
209
+ source={{
210
+ // TODO: create util function for generating checkered background
211
+ uri:
212
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAIUlEQVQoU2P8////fwYkwMjIyIjCp4MCZPtAbAwraa8AAEGrH/nfAIhgAAAAAElFTkSuQmCC',
213
+ }}
214
+ />
215
+ )}
216
+ </View>
217
+ )}
218
+ </View>
219
+ )}
220
+ </View>
221
+ );
222
+ };
223
+
224
+ const borderStyles = StyleSheet.create({
225
+ position: {
226
+ position: 'absolute',
227
+ top: 0,
228
+ bottom: 0,
229
+ left: 0,
230
+ right: 0,
231
+ },
232
+
233
+ invert: {
234
+ transform: [{ rotate: '180deg' }],
235
+ },
236
+ });
@@ -0,0 +1 @@
1
+ export { default } from './Button';
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react-native';
3
+
4
+ import { Card, Text } from '../..';
5
+
6
+ describe('<Card />', () => {
7
+ it('should render children', () => {
8
+ const { getByText } = render(
9
+ <Card>
10
+ <Text>Banana dance</Text>
11
+ </Card>,
12
+ );
13
+
14
+ expect(getByText('Banana dance')).toBeTruthy();
15
+ });
16
+
17
+ it('should render custom styles', () => {
18
+ const style = { backgroundColor: 'teal' };
19
+
20
+ const { getByTestId } = render(
21
+ <Card style={style} testID='card'>
22
+ <Text>Card</Text>
23
+ </Card>,
24
+ );
25
+
26
+ expect(getByTestId('card')).toHaveStyle(style);
27
+ });
28
+ });
29
+
30
+ describe('<Card.Content />', () => {
31
+ it('should render children', () => {
32
+ const { getByText } = render(
33
+ <Card>
34
+ <Card.Content>
35
+ <Text>Banana dance</Text>
36
+ </Card.Content>
37
+ </Card>,
38
+ );
39
+
40
+ expect(getByText('Banana dance')).toBeTruthy();
41
+ });
42
+
43
+ it('should render custom styles', () => {
44
+ const style = { backgroundColor: 'teal' };
45
+
46
+ const { getByTestId } = render(
47
+ <Card>
48
+ <Card.Content style={style} testID='content' />
49
+ </Card>,
50
+ );
51
+
52
+ expect(getByTestId('content')).toHaveStyle(style);
53
+ });
54
+ });
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import { StyleProp, StyleSheet, ViewStyle, View } from 'react-native';
3
+
4
+ import type { Theme } from '../../types';
5
+ import { withTheme } from '../../core/theming';
6
+
7
+ import CardContent from './CardContent';
8
+
9
+ // TODO: add following props:
10
+ // onLongPress,
11
+ // onPress,
12
+
13
+ type Props = React.ComponentPropsWithRef<typeof View> & {
14
+ children?: React.ReactNode;
15
+ elevation?: number;
16
+ style?: StyleProp<ViewStyle>;
17
+ theme: Theme;
18
+ };
19
+
20
+ const Card = ({
21
+ children,
22
+ elevation: elevationProp = 1,
23
+ style = {},
24
+ theme,
25
+ ...rest
26
+ }: Props) => {
27
+ const elevation = elevationProp * 2;
28
+ return (
29
+ <View style={[styles.wrapper, style]} {...rest}>
30
+ <View
31
+ style={[
32
+ styles.inner,
33
+ {
34
+ marginRight: elevation,
35
+ marginBottom: elevation,
36
+ },
37
+ ]}
38
+ >
39
+ {elevation !== 0 && (
40
+ <View
41
+ style={[
42
+ styles.shadow,
43
+ {
44
+ top: elevation,
45
+ left: elevation,
46
+ width: '100%',
47
+ height: '100%',
48
+ backgroundColor: theme.borderDarkest,
49
+ },
50
+ ]}
51
+ />
52
+ )}
53
+ <View
54
+ style={[
55
+ styles.card,
56
+ {
57
+ backgroundColor: theme.canvas,
58
+ borderColor: theme.borderDarkest,
59
+ },
60
+ ]}
61
+ >
62
+ {children}
63
+ </View>
64
+ </View>
65
+ </View>
66
+ );
67
+ };
68
+
69
+ const styles = StyleSheet.create({
70
+ wrapper: {
71
+ flex: 1,
72
+ position: 'relative',
73
+ },
74
+ inner: {
75
+ flexGrow: 1,
76
+ },
77
+ card: {
78
+ borderWidth: 2,
79
+ flexGrow: 1,
80
+ },
81
+ shadow: {
82
+ position: 'absolute',
83
+ },
84
+ });
85
+
86
+ Card.Content = CardContent;
87
+
88
+ export default withTheme(Card);
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { StyleProp, StyleSheet, ViewStyle, View } from 'react-native';
3
+
4
+ type Props = React.ComponentPropsWithRef<typeof View> & {
5
+ children?: React.ReactNode;
6
+ style?: StyleProp<ViewStyle>;
7
+ };
8
+
9
+ const CardContent = ({ children, style = {}, ...rest }: Props) => {
10
+ return (
11
+ <View style={[styles.wrapper, style]} {...rest}>
12
+ {children}
13
+ </View>
14
+ );
15
+ };
16
+
17
+ const styles = StyleSheet.create({
18
+ wrapper: {
19
+ padding: 8,
20
+ },
21
+ });
22
+
23
+ export default CardContent;
@@ -0,0 +1 @@
1
+ export { default } from './Card';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { SwitchBase, SwitchProps } from '../SwitchBase';
3
+
4
+ import { withTheme } from '../../core/theming';
5
+
6
+ const Checkbox = (props: SwitchProps) => {
7
+ return <SwitchBase component='checkbox' {...props} />;
8
+ };
9
+
10
+ export default withTheme(Checkbox);
@@ -0,0 +1 @@
1
+ export { default } from './Checkbox';
@@ -0,0 +1,69 @@
1
+ import React from 'react';
2
+ import { StyleSheet, View } from 'react-native';
3
+
4
+ import type { Theme, $RemoveChildren, Color } from '../../types';
5
+ import { withTheme } from '../../core/theming';
6
+
7
+ import { Button, Divider, ArrowIcon } from '../..';
8
+
9
+ type Props = $RemoveChildren<typeof Button> & {
10
+ color?: Color;
11
+ theme: Theme;
12
+ };
13
+
14
+ const previewHeight = 20;
15
+
16
+ const ColorButton = ({ disabled, color, theme, ...rest }: Props) => {
17
+ return (
18
+ <Button variant='raised' disabled={disabled} {...rest}>
19
+ <View style={styles.row}>
20
+ <View
21
+ style={[
22
+ styles.colorPreview,
23
+ {
24
+ backgroundColor: color || 'transparent',
25
+ borderWidth: 2,
26
+ borderColor: disabled
27
+ ? theme.materialTextDisabled
28
+ : theme.materialText,
29
+ shadowColor: disabled
30
+ ? theme.materialTextDisabledShadow
31
+ : 'transparent',
32
+ shadowOffset: {
33
+ width: 1,
34
+ height: 1,
35
+ },
36
+ shadowOpacity: 1,
37
+ shadowRadius: 0,
38
+ },
39
+ ]}
40
+ />
41
+ <Divider orientation='vertical' size={previewHeight} />
42
+ <ArrowIcon
43
+ theme={theme}
44
+ direction='down'
45
+ disabled={disabled}
46
+ segments={3}
47
+ style={styles.dropdownIcon}
48
+ />
49
+ </View>
50
+ </Button>
51
+ );
52
+ };
53
+
54
+ const styles = StyleSheet.create({
55
+ row: {
56
+ flexDirection: 'row',
57
+ },
58
+ colorPreview: {
59
+ width: 36,
60
+ marginRight: 6,
61
+ },
62
+ dropdownIcon: {
63
+ alignSelf: 'center',
64
+ marginRight: 2,
65
+ marginLeft: 4,
66
+ },
67
+ });
68
+
69
+ export default withTheme(ColorButton);
@@ -0,0 +1 @@
1
+ export { default } from './ColorButton';
@@ -0,0 +1,109 @@
1
+ import React from 'react';
2
+ import {
3
+ StyleSheet,
4
+ TouchableHighlight,
5
+ View,
6
+ StyleProp,
7
+ ViewStyle,
8
+ } from 'react-native';
9
+ import type { Theme, Color } from '../../types';
10
+ import { withTheme } from '../../core/theming';
11
+
12
+ import { Border } from '../../styles/styleElements';
13
+
14
+ const colorPreviewSize = 34;
15
+
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ function chunk(arr: any[], chunkSize: number) {
18
+ const R = [];
19
+ for (let i = 0, len = arr.length; i < len; i += chunkSize)
20
+ R.push(arr.slice(i, i + chunkSize));
21
+ return R;
22
+ }
23
+
24
+ type Props = {
25
+ colors: Color[];
26
+ colorsPerRow?: number;
27
+ onChange?: (value: Color) => void;
28
+ style?: StyleProp<ViewStyle>;
29
+ theme: Theme;
30
+ value?: Color;
31
+ wide?: boolean;
32
+ };
33
+
34
+ const ColorPicker = ({
35
+ colors,
36
+ colorsPerRow = 5,
37
+ onChange,
38
+ style,
39
+ theme,
40
+ value,
41
+ wide = false,
42
+ ...rest
43
+ }: Props) => {
44
+ const handleColorPress = (v: Color) => {
45
+ onChange?.(v);
46
+ };
47
+ const rows = chunk(colors, colorsPerRow);
48
+
49
+ return (
50
+ <View style={style} {...rest}>
51
+ {rows.map((rowColors, i) => (
52
+ <View style={styles.row} key={i}>
53
+ {rowColors.map(color => {
54
+ // TODO: pick more visible cborders olors for selected item
55
+ const isSelected = color === value;
56
+ return (
57
+ <TouchableHighlight
58
+ key={color}
59
+ onPress={() => handleColorPress(color)}
60
+ style={[
61
+ styles.colorPreview,
62
+ {
63
+ width: (wide ? 1.4 : 1) * colorPreviewSize,
64
+ height: colorPreviewSize,
65
+ borderWidth: 2,
66
+ borderColor: isSelected
67
+ ? theme.materialText
68
+ : 'transparent',
69
+ },
70
+ ]}
71
+ >
72
+ <View
73
+ style={{
74
+ flex: 1,
75
+ backgroundColor: color,
76
+ borderWidth: isSelected ? 2 : 0,
77
+ borderColor: theme.canvas,
78
+ }}
79
+ >
80
+ <View
81
+ style={{
82
+ flex: 1,
83
+ borderWidth: isSelected ? 2 : 0,
84
+ borderColor: theme.materialText,
85
+ }}
86
+ >
87
+ {!isSelected && <Border variant='cutout' theme={theme} />}
88
+ </View>
89
+ </View>
90
+ </TouchableHighlight>
91
+ );
92
+ })}
93
+ </View>
94
+ ))}
95
+ </View>
96
+ );
97
+ };
98
+
99
+ const styles = StyleSheet.create({
100
+ row: {
101
+ flexDirection: 'row',
102
+ },
103
+ colorPreview: {
104
+ width: colorPreviewSize,
105
+ height: colorPreviewSize,
106
+ },
107
+ });
108
+
109
+ export default withTheme(ColorPicker);
@@ -0,0 +1 @@
1
+ export { default } from './ColorPicker';
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react-native';
3
+
4
+ import { Desktop, Text } from '../..';
5
+
6
+ describe('<Desktop />', () => {
7
+ it('should render children in "screen"', () => {
8
+ const { getByTestId } = render(
9
+ <Desktop>
10
+ <Text>Desktop content</Text>
11
+ </Desktop>,
12
+ );
13
+
14
+ expect(getByTestId('desktopScreen')).toHaveTextContent('Desktop content');
15
+ });
16
+
17
+ it('should render custom styles', () => {
18
+ const style = { backgroundColor: 'teal' };
19
+
20
+ const { getByTestId } = render(<Desktop style={style} testID='card' />);
21
+
22
+ expect(getByTestId('card')).toHaveStyle(style);
23
+ });
24
+
25
+ it('should render custom screenStyles', () => {
26
+ const style = { backgroundColor: 'teal' };
27
+
28
+ const { getByTestId } = render(<Desktop screenStyle={style} />);
29
+
30
+ expect(getByTestId('desktopScreen')).toHaveStyle(style);
31
+ });
32
+ });