-
Notifications
You must be signed in to change notification settings - Fork 734
Feat/chips input v2 #1490
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/chips input v2 #1490
Changes from 26 commits
0468453
ed9235c
dec823a
56f253e
6a66baf
784001b
eefbbc2
d4487db
3cc26e2
3f7f91a
6dbb8fa
1c8e43a
17944ea
a6c176e
3cf3ced
93123d0
01ca150
a44e00e
fbffefa
9585e78
650e717
38c6608
d8537cf
ce1915a
9fb87b1
61689ca
e098955
85aa6f2
532f470
71b9449
2bb2ffa
d04fb89
bfb8309
a6f9242
5d385b8
e0f0db0
3497126
d5b3bc6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,31 +2,82 @@ import React, {Component} from 'react'; | |
import {StyleSheet, ScrollView} from 'react-native'; | ||
import {View, Colors, Text, Typography, ChipsInput} from 'react-native-ui-lib'; // eslint-disable-line | ||
|
||
interface State { | ||
chips: Array<any>; | ||
tags: Array<any>; | ||
tags2: Array<string>; | ||
tags3: Array<string>; | ||
tags4: Array<string>; | ||
} | ||
|
||
export default class ChipsInputScreen extends Component<{}, State> { | ||
customChipsInput: typeof ChipsInput; | ||
|
||
export default class ChipsInputScreen extends Component { | ||
constructor(props) { | ||
constructor(props: any) { | ||
super(props); | ||
|
||
this.state = { | ||
chips: [{label: 'Falcon 9'}, {label: 'Enterprise'}, {label: 'Challenger'}, {label: 'Coca Cola', invalid: true}], | ||
tags: [{label: 'Amit'}, {label: 'Ethan', invalid: true}], | ||
tags2: ['Chips', 'Input'], | ||
tags3: ['Non', 'Removable', 'Tags'], | ||
tags4: ['Change', 'Typography'] | ||
}; | ||
} | ||
|
||
onTagPress = (tagIndex, markedTagIndex) => { | ||
onTagPress = (tagIndex: number, markedTagIndex: number) => { | ||
this.customChipsInput.markTagIndex(tagIndex === markedTagIndex ? undefined : tagIndex); | ||
}; | ||
|
||
renderCustomTag(tag, index, shouldMarkToRemove) { | ||
renderCustomTag(tag: any, _: any, shouldMarkToRemove: boolean) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. index param is of type number, not any |
||
return ( | ||
<View style={[styles.customTag, shouldMarkToRemove && {backgroundColor: Colors.purple70}]}> | ||
<Text white>{tag.label}</Text> | ||
</View> | ||
); | ||
} | ||
|
||
renderLeftElement = () => { | ||
return ( | ||
<View center height={40} marginR-s2 style={{alignItems: 'flex-start'}}> | ||
<Text grey30 text70M> | ||
To: | ||
</Text> | ||
</View> | ||
); | ||
}; | ||
|
||
renderSearchTypeInput = () => { | ||
return ( | ||
<> | ||
<Text marginB-10 text60>Search Type</Text> | ||
<View bg-grey60> | ||
<ChipsInput | ||
placeholder={'Enter Tags'} | ||
chips={this.state.chips} | ||
leftElement={this.renderLeftElement()} | ||
hideUnderline | ||
maxHeight={100} | ||
/> | ||
</View> | ||
</> | ||
); | ||
}; | ||
|
||
renderFormTypeInput = () => { | ||
return ( | ||
<View marginT-40> | ||
<Text marginB-10 text60>Form Type</Text> | ||
<ChipsInput | ||
placeholder={'Enter Tags'} | ||
title={'Mendy'} | ||
chips={this.state.chips} | ||
maxLength={4} | ||
/> | ||
</View> | ||
); | ||
}; | ||
|
||
render() { | ||
return ( | ||
<ScrollView keyboardShouldPersistTaps="never"> | ||
|
@@ -35,12 +86,12 @@ export default class ChipsInputScreen extends Component { | |
ChipsInput | ||
</Text> | ||
|
||
<ChipsInput | ||
containerStyle={{marginBottom: 25}} | ||
placeholder="Enter Tags" | ||
tags={this.state.tags2} | ||
/> | ||
|
||
{this.renderSearchTypeInput()} | ||
|
||
{this.renderFormTypeInput()} | ||
|
||
<Text text50 marginV-20>Old Usage</Text> | ||
Inbal-Tish marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<ChipsInput | ||
containerStyle={{marginBottom: 25}} | ||
placeholder="Enter Tags" | ||
|
@@ -57,12 +108,12 @@ export default class ChipsInputScreen extends Component { | |
/> | ||
|
||
<ChipsInput | ||
ref={r => (this.customChipsInput = r)} | ||
ref={(r: typeof ChipsInput) => (this.customChipsInput = r)} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't you use React.createRef()? |
||
containerStyle={{marginBottom: 25}} | ||
placeholder="With custom tags" | ||
tags={this.state.tags} | ||
renderTag={this.renderCustomTag} | ||
onCreateTag={value => ({label: value})} | ||
onCreateTag={(value: string) => ({label: value})} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move to a class method please |
||
onTagPress={this.onTagPress} | ||
inputStyle={{...Typography.text60, color: Colors.blue30}} | ||
Inbal-Tish marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { ChipProps, ChipsInputProps } from './index'; | ||
export declare const isContainsInvalid: (chips: Array<ChipProps>) => boolean; | ||
export declare const getValidationBasedColor: (chips: Array<ChipProps>, defaultChip?: ChipProps | undefined) => string; | ||
export declare const getCounterTextColor: (stateChips: Array<ChipProps>, props: ChipsInputProps) => string; | ||
export declare const getCounterText: (count: number, maxLength: number) => string; | ||
export declare const getChipDismissColor: (chip: ChipProps, isSelected: boolean, defaultChipProps?: ChipProps | undefined) => string; | ||
export declare const isDisabled: (props: ChipsInputProps) => boolean; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import _ from 'lodash'; | ||
import React, { Component } from 'react'; | ||
import { ViewStyle, TextInput, NativeSyntheticEvent, TextInputKeyPressEventData, ScrollView, ScrollViewProps, TextInputProps as RNTextInputProps } from 'react-native'; | ||
import { BaseComponentInjectedProps, TypographyModifiers } from '../../commons/new'; | ||
import { ChipProps as ExternalChipProp } from '../chip'; | ||
import { TextFieldProps } from '../../../typings/components/Inputs'; | ||
declare type ChipType = string | boolean | any; | ||
export declare type ChipProps = ExternalChipProp & { | ||
invalid?: boolean; | ||
}; | ||
export declare type ChipsInputProps = TypographyModifiers & TextFieldProps & { | ||
/** | ||
* DEPRECATED: use chips instead. list of tags. can be string boolean or custom object when implementing getLabel | ||
*/ | ||
tags?: Array<ChipType>; | ||
/** | ||
* list of tags. can be string boolean or custom object when implementing getLabel | ||
*/ | ||
chips?: Array<ChipProps>; | ||
/** | ||
* Style your chips | ||
*/ | ||
defaultChipProps?: ChipProps; | ||
/** | ||
* callback for extracting the label out of the tag item | ||
*/ | ||
getLabel?: (tag: ChipType) => any; | ||
/** | ||
* callback for custom rendering tag item | ||
*/ | ||
renderTag?: (tag: ChipType, index: number, shouldMarkTag: boolean, label: string) => React.ReactElement; | ||
/** | ||
* callback for onChangeTags event | ||
*/ | ||
onChangeTags?: () => void; | ||
/** | ||
* callback for creating new tag out of input value (good for composing tag object) | ||
*/ | ||
onCreateTag?: (value: any) => void; | ||
/** | ||
* callback for when pressing a tag in the following format (tagIndex, markedTagIndex) => {...} | ||
*/ | ||
onTagPress?: (index: number, toRemove?: number) => void; | ||
/** | ||
* validation message error appears when tag isn't validate | ||
*/ | ||
validationErrorMessage?: string; | ||
/** | ||
* if true, tags *removal* Ux won't be available | ||
*/ | ||
disableTagRemoval?: boolean; | ||
/** | ||
* if true, tags *adding* Ux (i.e. by 'submitting' the input text) won't be available | ||
*/ | ||
disableTagAdding?: boolean; | ||
/** | ||
* custom styling for the component container | ||
*/ | ||
containerStyle?: ViewStyle; | ||
/** | ||
* custom styling for the tag item | ||
*/ | ||
tagStyle?: ViewStyle; | ||
/** | ||
* custom styling for the text input | ||
*/ | ||
inputStyle?: RNTextInputProps['style']; | ||
/** | ||
* should hide input underline | ||
*/ | ||
hideUnderline?: boolean; | ||
/** | ||
* Maximum numbers of chips | ||
*/ | ||
maxLength?: number; | ||
/** | ||
* Chips with maxHeigh is inside a scrollView | ||
*/ | ||
scrollViewProps?: ScrollViewProps; | ||
/** | ||
* Chips inside a ScrollView | ||
*/ | ||
maxHeight?: number; | ||
/** | ||
* Custom element before the chips, for example 'search' icon, 'To:' label etc' | ||
*/ | ||
leftElement?: JSX.Element | JSX.Element[]; | ||
value?: any; | ||
selectionColor?: string | number; | ||
}; | ||
declare type State = { | ||
value: any; | ||
chips: Array<ChipType>; | ||
chipIndexToRemove?: number; | ||
initialChips?: Array<ChipType>; | ||
isFocused: boolean; | ||
}; | ||
declare type OwnProps = ChipsInputProps & BaseComponentInjectedProps; | ||
/** | ||
* @description: Tags input component (chips) | ||
* @modifiers: Typography | ||
* @gif: https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/ChipsInput/ChipsInput.gif?raw=true | ||
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/ChipsInputScreen.js | ||
* @extends: TextField | ||
*/ | ||
declare class ChipsInput extends Component<OwnProps, State> { | ||
static displayName: string; | ||
static onChangeTagsActions: { | ||
ADDED: string; | ||
REMOVED: string; | ||
}; | ||
input: React.RefObject<TextInput>; | ||
scrollRef: React.RefObject<ScrollView>; | ||
constructor(props: OwnProps); | ||
componentDidMount(): void; | ||
static getDerivedStateFromProps(nextProps: Readonly<OwnProps>, prevState: State): { | ||
initialChips: any[] | undefined; | ||
chips: any[] | undefined; | ||
} | null; | ||
addTag: () => void; | ||
removeMarkedTag(): void; | ||
markTagIndex: (chipIndex: number) => void; | ||
onChangeText: _.DebouncedFunc<(value: any) => void>; | ||
onTagPress(index: number): void; | ||
isLastTagMarked(): boolean; | ||
onKeyPress: (event: NativeSyntheticEvent<TextInputKeyPressEventData>) => void; | ||
getLabel: (item: ChipType) => any; | ||
onFocus: () => void; | ||
onBlur: () => void; | ||
renderLabel(tag: ChipType, shouldMarkTag: boolean): JSX.Element; | ||
renderTag: (tag: ChipType, index: number) => JSX.Element; | ||
renderTagWrapper: (tag: ChipType, index: number) => JSX.Element; | ||
renderNewChip: () => JSX.Element[]; | ||
renderTitleText: () => "" | JSX.Element | undefined; | ||
renderChips: () => JSX.Element[]; | ||
renderCharCounter(): JSX.Element | undefined; | ||
renderUnderline: () => JSX.Element; | ||
renderTextInput(): JSX.Element; | ||
renderChipsContainer: () => JSX.Element; | ||
render(): JSX.Element; | ||
blur(): void; | ||
focus(): void; | ||
clear(): void; | ||
} | ||
export { ChipsInput }; | ||
declare const _default: React.ComponentClass<ChipsInputProps & { | ||
useCustomTheme?: boolean | undefined; | ||
}, any> & typeof ChipsInput; | ||
export default _default; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import {Colors} from '../../style'; | ||
import {ChipProps, ChipsInputProps} from './index'; | ||
|
||
export const hasInvalidChip = (chips: Array<ChipProps>) => { | ||
return chips.filter((chip) => chip.invalid === true)[0] !== undefined; | ||
}; | ||
|
||
export const getValidationBasedColor = (chips: Array<ChipProps>, defaultChip?: ChipProps) => { | ||
const dismissColor = defaultChip?.dismissColor || Colors.red30; | ||
|
||
return hasInvalidChip(chips) ? dismissColor : Colors.primary; | ||
}; | ||
|
||
export const getCounterTextColor = (stateChips: Array<ChipProps>, props: ChipsInputProps) => { | ||
const {maxLength} = props; | ||
if (isDisabled(props)) { | ||
return Colors.grey50; | ||
} | ||
return maxLength && stateChips.length >= maxLength ? Colors.red30 : Colors.grey30; | ||
}; | ||
|
||
export const getCounterText = (count: number, maxLength: number) => { | ||
return `${Math.min(count, maxLength)} / ${maxLength}`; | ||
}; | ||
|
||
export const getChipDismissColor = (chip: ChipProps, isSelected: boolean, defaultChipProps?: ChipProps) => { | ||
Inbal-Tish marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const dismissColor = defaultChipProps?.dismissColor || Colors.white; | ||
return !chip.invalid ? dismissColor : isSelected ? Colors.red10 : Colors.red30; | ||
}; | ||
|
||
export const isDisabled = (props: ChipsInputProps) => { | ||
const {disableTagRemoval, editable} = props; | ||
return disableTagRemoval || editable === false; | ||
}; | ||
|
Uh oh!
There was an error while loading. Please reload this page.