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
All checks were successful
forgejo/Procyon/procyon/pipeline/head This commit looks good
This commit is contained in:
parent
451adb4942
commit
fbbdc3825c
6 changed files with 51 additions and 23 deletions
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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'];
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Reference in a new issue