Skip to content

Commit

Permalink
feat(colorpicker): add colorpicker component
Browse files Browse the repository at this point in the history
  • Loading branch information
Artur Bien committed Jan 1, 2021
1 parent 179a7c6 commit e1fe103
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 16 deletions.
87 changes: 87 additions & 0 deletions example/src/examples/ColorPickerExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* eslint-disable no-console */
import React from 'react';
import { View } from 'react-native';
import {
ColorButton,
Menu,
ColorPicker,
Divider,
Button,
} from 'react95-native';

import Container from '../util/Container';

const colors = [
'white',
'black',
'#bdbebd',
'#7a7d7b',
'#ff0102',
'#7c0000',
'#feff00',
'#7c7d04',
'#00ff00',
'#077d04',
'#0afeff',
'#067d7b',
'#1402ff',
'#05007b',
'#ff01ff',
'#7a037b',
'#cdaeb4',
'#9b5d6a',
'#e5d6de',
'#9c9d9d',
];

const ColorPickerExample = () => {
const [colorMenuOpen, setColorMenuOpen] = React.useState(true);
const [color, setColor] = React.useState(colors[3]);

const handleColorChange = (value: string) => {
setColor(value);
setColorMenuOpen(false);
};
return (
<Container>
<Container.Section title='Default'>
<View
style={{
width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
}}
>
<Menu
open={colorMenuOpen}
anchor={
<ColorButton
onPress={() => setColorMenuOpen(state => !state)}
color={color}
/>
}
>
<ColorPicker
onChange={handleColorChange}
value={color}
colors={colors}
colorsPerRow={5}
/>
<Divider style={{ marginVertical: 4 }} />
<Button
disabled
style={{
margin: 2,
}}
>
Other...
</Button>
</Menu>
<ColorButton color='red' disabled />
</View>
</Container.Section>
</Container>
);
};

export default ColorPickerExample;
36 changes: 22 additions & 14 deletions example/src/examples/ScrollPanelExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,30 @@ import React from 'react';
import { View, StyleSheet } from 'react-native';
import { ScrollPanel } from 'react95-native';

const getRandomColor = () => {
const letters = '0123456789ABCDEF';
let color = '#';

Array(6)
.fill(null)
.forEach(() => {
color += letters[Math.floor(Math.random() * 16)];
});

return color;
};
const colors = [
'white',
'black',
'#bdbebd',
'#7a7d7b',
'#ff0102',
'#7c0000',
'#feff00',
'#7c7d04',
'#00ff00',
'#077d04',
'#0afeff',
'#067d7b',
'#1402ff',
'#05007b',
'#ff01ff',
'#7a037b',
'#cdaeb4',
'#9b5d6a',
'#e5d6de',
'#9c9d9d',
];

const ScrollPanelExample = () => {
const colors = new Array(16).fill(null).map(getRandomColor);

return (
<View style={{ backgroundColor: 'teal', flex: 1 }}>
<ScrollPanel>
Expand Down
10 changes: 8 additions & 2 deletions example/src/examples/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import AppBarExample from './AppBarExample';
import ButtonExample from './ButtonExample';
import CardExample from './CardExample';
import CheckboxExample from './CheckboxExample';
import ColorPickerExample from './ColorPickerExample';
import CutoutExample from './CutoutExample';
import DesktopExample from './DesktopExample';
import DividerExample from './DividerExample';
import FieldsetExample from './FieldsetExample';
import HourglassExample from './HourglassExample';
import ListExample from './ListExample';
import MenuExample from './MenuExample';
import NumberInputExample from './NumberInputExample';
import PanelExample from './PanelExample';
import ProgressExample from './ProgressExample';
import RadioExample from './RadioExample';
Expand All @@ -18,9 +20,8 @@ import SelectBoxExample from './SelectBoxExample';
import SelectExample from './SelectExample';
import SnackbarExample from './SnackbarExample';
import TabsExample from './TabsExample';
import TypographyExample from './TypographyExample';
import TextInputExample from './TextInputExample';
import NumberInputExample from './NumberInputExample';
import TypographyExample from './TypographyExample';
import WindowExample from './WindowExample';

export default [
Expand All @@ -33,6 +34,11 @@ export default [
component: CheckboxExample,
title: 'Checkbox',
},
{
name: 'ColorPickerExample',
component: ColorPickerExample,
title: 'ColorPicker',
},
{
name: 'NumberInputExample',
component: NumberInputExample,
Expand Down
77 changes: 77 additions & 0 deletions src/ColorButton/ColorButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, { useContext } from 'react';
import { StyleSheet, View, Image } from 'react-native';
import type { $RemoveChildren, Color } from '../types';

import { Button, Divider } from '..';
import { ThemeContext } from '../common/theming/Theme';

const dropdownSymbol = {
default:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAALElEQVQoU2NkIAIwEqGGgWRF/7GYCjYE3SRkhXA5bNaBFKKIk+wmnB4lyiQAAsgDCqkPxTcAAAAASUVORK5CYII=',
disabled:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAANklEQVQoU2NkIAIwEqGGgTRFLa0t/9FNramuARuCYhKyQpgCDEUgAZBCZAVYFWHzCGkOxxcUANHnDAplQ9G1AAAAAElFTkSuQmCC',
};

type Props = $RemoveChildren<typeof Button> & {
color?: Color;
};

const previewHeight = 20;

const ColorButton = ({ disabled, color, ...rest }: Props) => {
const theme = useContext(ThemeContext);

return (
<Button variant='outside' disabled={disabled} {...rest}>
<View style={styles.row}>
<View
style={[
styles.colorPreview,
{
backgroundColor: color || 'transparent',
borderWidth: 2,
borderColor: disabled
? theme.materialTextDisabled
: theme.materialText,
shadowColor: disabled
? theme.materialTextDisabledShadow
: 'transparent',
shadowOffset: {
width: 1,
height: 1,
},
shadowOpacity: 1,
shadowRadius: 0,
},
]}
/>
<Divider orientation='vertical' size={previewHeight} />
<Image
style={styles.dropdownIcon}
source={{
uri: dropdownSymbol[disabled ? 'disabled' : 'default'],
}}
/>
</View>
</Button>
);
};

const styles = StyleSheet.create({
row: {
flexDirection: 'row',
},
colorPreview: {
width: 36,
marginRight: 6,
},
dropdownIcon: {
width: 20,
height: 20,
alignSelf: 'center',
marginRight: -4,
marginLeft: 1,
},
});

export default ColorButton;
1 change: 1 addition & 0 deletions src/ColorButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ColorButton';
105 changes: 105 additions & 0 deletions src/ColorPicker/ColorPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React, { useContext } from 'react';
import {
StyleSheet,
TouchableHighlight,
View,
StyleProp,
ViewStyle,
} from 'react-native';
import type { Color } from '../types';

import { Border } from '../common/styleElements';
import { ThemeContext } from '../common/theming/Theme';

const colorPreviewSize = 34;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function chunk(arr: any[], chunkSize: number) {
const R = [];
for (let i = 0, len = arr.length; i < len; i += chunkSize)
R.push(arr.slice(i, i + chunkSize));
return R;
}

type Props = {
colors: Color[];
colorsPerRow?: number;
onChange?: (value: Color) => void;
style?: StyleProp<ViewStyle>;
value?: Color;
};

const ColorPicker = ({
colors,
colorsPerRow = 5,
onChange,
style,
value,
...rest
}: Props) => {
const theme = useContext(ThemeContext);

const handleColorPress = (v: Color) => {
onChange?.(v);
};
const rows = chunk(colors, colorsPerRow);

return (
<View style={style} {...rest}>
{rows.map((rowColors, i) => (
<View style={styles.row} key={i}>
{rowColors.map(color => {
// TODO: pick more visible cborders olors for selected item
const isSelected = color === value;
return (
<TouchableHighlight
key={color}
onPress={() => handleColorPress(color)}
style={[
styles.color,
{
borderWidth: 2,
borderColor: isSelected
? theme.materialText
: 'transparent',
},
]}
>
<View
style={{
flex: 1,
backgroundColor: color,
borderWidth: isSelected ? 2 : 0,
borderColor: theme.canvas,
}}
>
<View
style={{
flex: 1,
borderWidth: isSelected ? 2 : 0,
borderColor: theme.materialText,
}}
>
{!isSelected && <Border variant='cutout' />}
</View>
</View>
</TouchableHighlight>
);
})}
</View>
))}
</View>
);
};

const styles = StyleSheet.create({
row: {
flexDirection: 'row',
},
color: {
width: colorPreviewSize,
height: colorPreviewSize,
},
});

export default ColorPicker;
1 change: 1 addition & 0 deletions src/ColorPicker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ColorPicker';
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export { default as Button } from './Button';
export { default as ColorButton } from './ColorButton';
export { default as ColorPicker } from './ColorPicker';
export { default as TextInput } from './TextInput';
export { default as NumberInput } from './NumberInput';
export { default as Panel } from './Panel';
Expand Down
3 changes: 3 additions & 0 deletions src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export type $RemoveChildren<T extends React.ComponentType<any>> = $Omit<
'children'
>;

// TODO: proper Color type
export type Color = string;

export type Sizes = 'sm' | 'md' | 'lg';

export type OrientationProp = 'horizontal' | 'vertical';
Expand Down

0 comments on commit e1fe103

Please sign in to comment.