Skip to content

Commit

Permalink
feat(select): enables custom picker
Browse files Browse the repository at this point in the history
  • Loading branch information
erick-martins committed Jun 12, 2024
1 parent 66e8174 commit c059069
Show file tree
Hide file tree
Showing 12 changed files with 513 additions and 123 deletions.
71 changes: 71 additions & 0 deletions docs/components/toast.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@

# Toast dialog

O Toast Dialog é usado com o hook `useToastDialog`, definido em `mobile-ui/toast/provider.tsx`.

Ele retorna um objeto contendo duas funções, `present` e `dismiss`.

### Como usar:

```typescript
import React, { useState } from 'react';
import { useToastDialog } from '@freud-ds/react';
import { Button, View } from 'react-native';

export const Example: React.FC = () => {
const { present } = useToastDialog();

const presentToast = useCallback(() => {
present({
status: 'warning',
title: 'Tost Example',
body: 'The quick brown fox jumps over the lazy dog',
dismissible: true,
onDismiss: () => {},
style: {},
});
}, [present]);

return (
<View>
<Button title="Present Toast" onPress={displayToastPrimary('info')} />
</View>
);
};
```

### Função `present`

Esta é a função usada para exibir um toast, tem com retorno a ID do gerada dinamicamente;

Return Type: `string` (Toast ID)

Arguments:

- config: `ToastDialogConfig` - Interface declarada em `mobile-ui/toast/types.d.ts`

**Propriedade do `ToastDialogConfig`**

| Property | Type | Description | Required | Default |
| ------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | ------ |
| `title` | `string` | Texto do título do toast. | ✓ (obrigatório apenas se não existir `body`) | |
| `body` | `string` | Texto do corpo do toast. | ✓ (obrigatório apenas se não existir `title`) | |
| `status` | `'success' | 'info' | 'warning' | 'danger'` | Status do toast. <br />Para cada status, temos cores e ícones diferentes. | | `info` |
| `dismissible` | `boolean` | Determina se o toast é dispensável. <br />Quando `true`:<br />- apresenta um botão de para fechar;<br />- roda o timeout para fechar automaticamente, com exceção nos casos em que `duration="permanent"`. | | `true` |
| `duration` | `number | 'permanent'` | Tempo em milisegundos para dispensar o toast. Aceita o valor`permanent` para não dispensar automaticamente. | | `5000`<br /><br />_<small>5000 milissegundos, este valor está declarado na constante `TOAST_DEFAULT_DURATION` localizado em `mobile-ui/toast/constants.ts`</small>_ |
| `onDismiss` | `() => void` | Callback que será disparado sempre que o toast for dispensado | | |
| `style` | `ViewStyle | AnimatedViewStyle` | Objeto `style` que será passado para o Toast | | |

### Função `dismiss`

Esta é a função usada para dispensar um toast em exibição

Return Type: `void`

Arguments:

- toastID: `string` - ID gerada dinamicamente para o toast em exibição, retornado pela função `present`.

### Toast Dialog nos seus temas

<img src="./assets/toasts-dialog.png" alt="radio" align=left />
26 changes: 4 additions & 22 deletions src/components/select/ios-picker-view.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,14 @@
import React, { memo, useCallback } from 'react';
import { requireNativeComponent, Platform } from 'react-native';
import {
requireNativeComponent,
TextInputProps,
NativeSyntheticEvent,
Platform,
} from 'react-native';
import { SelectOption } from './select.types';
FreudDSPickerViewChangeEvent,
FreudDSPickerViewProps,
} from './select.types';

const RNFreudDSIOSPickerView = Platform.select({
ios: requireNativeComponent('RNFreudDSPickerView'),
}) as React.ComponentType<any>;

export interface FreudDSPickerRenderInputProps extends TextInputProps {
nativeID: string;
value?: SelectOption['value'];
}

export interface FreudDSPickerViewProps {
selected?: SelectOption['value'];
options: SelectOption[];
inputNativeID: string;
onSelectedValueChange?: (selected?: SelectOption['value']) => void;
}

export type FreudDSPickerViewChangeEvent = NativeSyntheticEvent<{
value?: SelectOption['value'];
}>;

export const FreudDSIOSPickerView: React.FC<FreudDSPickerViewProps> = memo(
({ onSelectedValueChange, ...props }) => {
const onValueChangeHandler = useCallback(
Expand Down
138 changes: 137 additions & 1 deletion src/components/select/select.story.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { StoryWrapper } from '../../storybook/story-wrapper';
import { storiesOf } from '@storybook/react-native';
import React from 'react';
import React, { useState } from 'react';
import { FormControl } from '@components/form-control/form-control';
import { SelectOption } from './select.types';
import { Select } from './select';
import { useBooleanState } from '@hooks';
import { Text } from '@components/typography';
import { Modal, StyleSheet, View } from 'react-native';
import { Touchable } from '@components/touchable';
import { Icon } from '@components/icon';

const options: SelectOption[] = [
{ label: 'Option 1', value: 'option1' },
Expand All @@ -13,6 +18,135 @@ const options: SelectOption[] = [
{ label: 'Option 5', value: 'option5' },
];

interface CustomModalProps {
options: SelectOption[];
title: string;
visible: boolean;
selected?: string;
onSelectedValueChange: (selected?: SelectOption['value']) => void;
onDismiss: () => void;
}

const CustomModal: React.FC<CustomModalProps> = ({
title,
options,
selected,
onSelectedValueChange,
onDismiss,
visible = false,
}) => {
const onPressHandler = (option: SelectOption) => {
onSelectedValueChange(option.value);
};

const renderItem = (option: SelectOption) => {
const isSelected = option.value === selected;
return (
<Touchable
key={option.value}
data={option}
onPress={onPressHandler}
style={modalStyles.item}
>
<View style={modalStyles.selectedIconContainer}>
{isSelected ? <Icon size="sm" name="check" /> : null}
</View>
<Text style={isSelected ? modalStyles.textSelected : modalStyles.text}>
{option.label}
</Text>
</Touchable>
);
};
return (
<Modal visible={visible} onDismiss={onDismiss} transparent>
<View style={modalStyles.backdrop} onTouchEnd={onDismiss}>
<View style={modalStyles.container}>
<Text bold textAlign="center">
{title}
</Text>
<View style={modalStyles.itemsContainer}>
{options.map(renderItem)}
</View>
</View>
</View>
</Modal>
);
};

const modalStyles = StyleSheet.create({
backdrop: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
container: {
width: '70%',
backgroundColor: 'white',
paddingVertical: 16,
borderRadius: 8,
},
itemsContainer: {
display: 'flex',
borderTopColor: '#E0E0E0',
borderTopWidth: 1,
paddingTop: 8,
},
item: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 4,
paddingHorizontal: 16,
},
selectedIconContainer: {
width: 24,
justifyContent: 'center',
alignItems: 'center',
},
text: {
color: '#333',
},
textSelected: {
color: '#6732D1',
},
});

const CustomSelect: React.FC<{ nativeID: string; inverted?: boolean }> = ({
nativeID,
inverted = false,
}) => {
const [selected, setSelected] = useState<SelectOption['value'] | undefined>(
undefined
);
const modalVisibility = useBooleanState(false);

return (
<>
<Select
label="Custom Select"
placeholder="Placeholder"
helperText="Helper text"
nativeID={nativeID}
onPress={modalVisibility.toggle}
options={options}
selected={selected}
inverted={inverted}
customPicker
/>
<CustomModal
title="Select an option"
options={options}
selected={selected}
onDismiss={modalVisibility.setOff}
onSelectedValueChange={setSelected}
visible={modalVisibility.state}
/>
</>
);
};

const SelectStory = () => {
const [state, setState] = React.useState(
{} as Record<string, string | undefined>
Expand Down Expand Up @@ -73,6 +207,7 @@ const SelectStory = () => {
options={options}
disabled
/>
<CustomSelect nativeID="select-custom" />
</StoryWrapper.Session>
<StoryWrapper.Session paddingX={3} alignItems={undefined} inverted>
<Select
Expand Down Expand Up @@ -119,6 +254,7 @@ const SelectStory = () => {
disabled
inverted
/>
<CustomSelect nativeID="select-custom-inverted" inverted />
</StoryWrapper.Session>
</FormControl>
</StoryWrapper>
Expand Down
Loading

0 comments on commit c059069

Please sign in to comment.