Update List and Selectbox components to support different data id type and custom label rendering
All checks were successful
forgejo/Procyon/procyon/pipeline/head This commit looks good

This commit is contained in:
Roman Jaroš 2024-06-16 20:39:40 +00:00
parent 451adb4942
commit fbbdc3825c
6 changed files with 51 additions and 23 deletions

View file

@ -5,15 +5,16 @@ import { FlatList, GestureResponderEvent, StyleProp, ViewStyle } from 'react-nat
import { ListItem, ListItemProps } from '@procyon/components/ListItem'; import { ListItem, ListItemProps } from '@procyon/components/ListItem';
export type ListData = { export type ListData = {
id: number; id: number | string;
label: string; label?: string;
}; };
export type ListProps<Items extends ListData[]> = { export type ListProps<Items extends ListData[]> = {
Item?: FC<ListItemProps<Items[0]>>; Item?: FC<ListItemProps<Items[0] | ListData>>;
color?: 'primary' | 'secondary' | 'positive' | 'negative'; color?: 'primary' | 'secondary' | 'positive' | 'negative';
compact?: boolean; compact?: boolean;
data?: Items; data?: Items;
getLabel?: (items: Items[0]) => string;
mode?: 'view' | 'interactive'; mode?: 'view' | 'interactive';
onClick?: (event: GestureResponderEvent, id: ListData['id']) => void; onClick?: (event: GestureResponderEvent, id: ListData['id']) => void;
selected?: ListData['id']; selected?: ListData['id'];
@ -28,6 +29,7 @@ export const List = <Items extends ListData[]>({
style, style,
data, data,
selected, selected,
getLabel,
Item = ListItem, Item = ListItem,
...props ...props
}: ListProps<Items>) => { }: ListProps<Items>) => {
@ -38,10 +40,10 @@ export const List = <Items extends ListData[]>({
style={[style?.list]} style={[style?.list]}
renderItem={({ item }) => ( renderItem={({ item }) => (
<Item {...props} item={item} selected={item.id === selected}> <Item {...props} item={item} selected={item.id === selected}>
{item.label} {item.label ? item.label : getLabel?.(item)}
</Item> </Item>
)} )}
keyExtractor={(item) => item.label.toString()} keyExtractor={(item) => item.id.toString()}
/> />
</ListContainer> </ListContainer>
); );

View file

@ -5,19 +5,24 @@ import { GestureResponderEvent, StyleProp, View, ViewProps } from 'react-native'
import { useAnimatedRef } from 'react-native-reanimated'; import { useAnimatedRef } from 'react-native-reanimated';
import Values from 'values.js'; import Values from 'values.js';
import { ListData, ListProps } from '@procyon/components/List'; import { ListData } from '@procyon/components/List';
import { RippleEffect } from '@procyon/components/RippleEffect'; import { RippleEffect } from '@procyon/components/RippleEffect';
import { isDark } from '@procyon/utils/color'; import { isDark } from '@procyon/utils/color';
import { debounceAfter } from '@procyon/utils/debounce'; import { debounceAfter } from '@procyon/utils/debounce';
export type ListItemProps<Item> = { export type ListItemProps<Item extends ListData> = {
children: ReactNode; children: ReactNode;
color?: 'primary' | 'secondary' | 'positive' | 'negative';
compact?: boolean;
item: Item; item: Item;
mode?: 'view' | 'interactive';
onClick?: (event: GestureResponderEvent, id: ListData['id']) => void;
selected?: boolean; selected?: boolean;
size?: 'sm' | 'md' | 'lg' | 'xl';
style?: { style?: {
container?: StyleProp<ViewProps>; container?: StyleProp<ViewProps>;
}; };
} & Omit<ListProps<any>, 'selected' | 'data' | 'style' | 'Item'>; };
export const ListItem = <Item extends ListData>({ export const ListItem = <Item extends ListData>({
style, style,

View file

@ -11,11 +11,14 @@ import { InputElementType } from '@procyon/types/form';
import { TextField, TextFieldProps } from './TextField'; import { TextField, TextFieldProps } from './TextField';
export type SelectboxProps<Items extends ListData[]> = InputElementType<number> & { export type SelectboxProps<Items extends ListData[]> = InputElementType<
number | string
> & {
Input?: FC<TextFieldProps & { data: Items }>; Input?: FC<TextFieldProps & { data: Items }>;
Item?: FC<ListItemProps<Items[0]>>; Item?: FC<ListItemProps<Items[0]>>;
clearable?: boolean; clearable?: boolean;
data?: Items; data?: Items;
getLabel?: (items: Items[0]) => string;
}; };
export const Selectbox = <Items extends ListData[]>(props: SelectboxProps<Items>) => { export const Selectbox = <Items extends ListData[]>(props: SelectboxProps<Items>) => {
@ -28,6 +31,7 @@ export const Selectbox = <Items extends ListData[]>(props: SelectboxProps<Items>
data, data,
Input = FieldInput, Input = FieldInput,
Item, Item,
getLabel,
...others ...others
} = props; } = props;
@ -42,9 +46,10 @@ export const Selectbox = <Items extends ListData[]>(props: SelectboxProps<Items>
Item={Item} Item={Item}
selected={value} selected={value}
data={data} data={data}
onClick={(event, value) => { getLabel={getLabel}
onClick={(event, id) => {
event.preventDefault(); event.preventDefault();
onChange?.(event, value); onChange?.(event, id);
closeModal(); closeModal();
}} }}
style={{ style={{
@ -99,7 +104,14 @@ export const Selectbox = <Items extends ListData[]>(props: SelectboxProps<Items>
onFocus={(e) => { onFocus={(e) => {
others.onFocus?.(e, value); others.onFocus?.(e, value);
}} }}
Input={(props) => <Input {...props} data={data} value={value?.toString()} />} Input={(props) => (
<Input
{...props}
data={data as any}
getLabel={getLabel}
value={value?.toString()}
/>
)}
message={message} message={message}
onClick={handleOpenOptions} onClick={handleOpenOptions}
RightIcon={(props) => ( RightIcon={(props) => (
@ -114,10 +126,18 @@ export const Selectbox = <Items extends ListData[]>(props: SelectboxProps<Items>
); );
}; };
const FieldInput: FC<TextFieldProps & { data: SelectboxProps<any>['data'] }> = ({ const FieldInput = <Items extends ListData[]>({
value, value,
data, data,
}) => <FieldText>{data?.find((item) => item.id === value)?.label}</FieldText>; getLabel,
}: TextFieldProps & {
data?: SelectboxProps<Items>['data'];
getLabel?: SelectboxProps<Items>['getLabel'];
}) => {
const item = data?.find((item) => item.id === value && !!value);
if (!item) return null;
return <FieldText>{getLabel ? getLabel(item) : item.label}</FieldText>;
};
const FieldText = styled.Text<{ const FieldText = styled.Text<{
size?: SelectboxProps<any>['size']; size?: SelectboxProps<any>['size'];

View file

@ -36,7 +36,7 @@ export const Default: Story = {
onClick={onValueChange} onClick={onValueChange}
data={[ data={[
{ {
id: 1, id: 'aa',
label: 'Tiger', label: 'Tiger',
}, },
{ {
@ -114,7 +114,7 @@ export const Custom: Story = {
<ListItem item={item} color="negative"> <ListItem item={item} color="negative">
<ListItemView> <ListItemView>
<FlagIcon color="red" strokeWidth={1} /> <FlagIcon color="red" strokeWidth={1} />
<ListItemText style={[{ fontWeight: 'bold' }]}>{value}</ListItemText> <ListItemText style={[{ fontWeight: 'bold' }]}>{children}</ListItemText>
</ListItemView> </ListItemView>
</ListItem> </ListItem>
); );

View file

@ -40,8 +40,8 @@ export const Default: Story = {
name: 'name1', name: 'name1',
label: 'I am Selectbox', label: 'I am Selectbox',
data: [ data: [
{ value: 'item1', name: 'Item 1' }, { id: 'item1', label: 'Item 1' },
{ value: 'item2', name: 'Item 2' }, { id: 'item2', label: 'Item 2' },
], ],
}, },
render: function Component(args) { render: function Component(args) {

View file

@ -13,7 +13,7 @@ type FormData = {
checkbox: boolean; checkbox: boolean;
datePicker: Date; datePicker: Date;
radiobutton: string; radiobutton: string;
selectBox: string; selectBox: number;
text: string; text: string;
timePicker: Date; timePicker: Date;
}; };
@ -23,7 +23,7 @@ export const ExampleForm = () => {
text: yup.string().required(), text: yup.string().required(),
checkbox: yup.boolean().required(), checkbox: yup.boolean().required(),
radiobutton: yup.string().required(), radiobutton: yup.string().required(),
selectBox: yup.string().required(), selectBox: yup.number().required(),
datePicker: yup.date().required(), datePicker: yup.date().required(),
timePicker: yup.date().required(), timePicker: yup.date().required(),
}); });
@ -32,7 +32,7 @@ export const ExampleForm = () => {
schema, schema,
defaultValues: { defaultValues: {
text: 'Hello', text: 'Hello',
selectBox: 'item2', selectBox: 2,
datePicker: new Date(), datePicker: new Date(),
timePicker: new Date(), timePicker: new Date(),
checkbox: true, checkbox: true,
@ -73,9 +73,10 @@ export const ExampleForm = () => {
component="SELECT" component="SELECT"
name="selectBox" name="selectBox"
label="SelectBox" label="SelectBox"
getLabel={({ name }) => name}
data={[ data={[
{ value: 'item1', name: 'Item 1' }, { id: 1, name: 'Item 1' },
{ value: 'item2', name: 'Item 2' }, { id: 2, name: 'Item 2' },
]} ]}
/> />
</GridCol> </GridCol>