diff --git a/.gitignore b/.gitignore index 6d5f090..487bd12 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,6 @@ # .idea node_modules/ -dist +dist/ npm-debug.log yarn-error.log diff --git a/package.json b/package.json index 8025612..3c682a8 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,10 @@ "url": "git+https://github.com/dabakovich/react-native-controlled-mentions.git" }, "author": "David Tabaka ", + "contributors": [ + "David Tabaka", + "Denifer Santiago" + ], "license": "MIT", "devDependencies": { "@types/diff": "^4.0.2", diff --git a/src/components/mention-input.tsx b/src/components/mention-input.tsx index 5be83d4..98cf379 100644 --- a/src/components/mention-input.tsx +++ b/src/components/mention-input.tsx @@ -4,10 +4,10 @@ import { Text, TextInput, TextInputSelectionChangeEventData, - View, + View } from 'react-native'; -import { MentionInputProps, MentionPartType, Suggestion } from '../types'; +import { MentionInputProps, MentionPartType, Suggestion, PartData, Part } from '../types'; import { defaultMentionTextStyle, generateValueFromPartsAndChangedText, @@ -16,6 +16,13 @@ import { isMentionPartType, parseValue, } from '../utils'; +const AddOrEdit = (arr: S[], val: S, i: number) => { + if(i === -1) { + arr.push(val); + } else { + arr[i] = val; + } +} const MentionInput: FC = ( { @@ -27,15 +34,16 @@ const MentionInput: FC = ( inputRef: propInputRef, containerStyle, - + defaultPartsData = [], onSelectionChange, - + onChangePartsData, ...textInputProps - }, + } : MentionInputProps, ) => { const textInput = useRef(null); const [selection, setSelection] = useState({start: 0, end: 0}); + const [partsData, setPartsData] = useState(defaultPartsData); const { plainText, @@ -54,9 +62,38 @@ const MentionInput: FC = ( * @param changedText */ const onChangeInput = (changedText: string) => { - onChange(generateValueFromPartsAndChangedText(parts, plainText, changedText)); + const newValue = generateValueFromPartsAndChangedText(parts, plainText, changedText); + const { parts: newParts } = parseValue(newValue, partTypes); + partDataHasChanged(newParts); + onChange(newValue); + }; + /** + * Determines when the onChangePartsData event should triggerred + * @param newParts + */ + const partDataHasChanged = (newParts: Part[]) => { + const partsWithData = newParts.filter(part => part.partType && isMentionPartType(part.partType)); + const newPartsData = partsWithData.reduce((acc, part) => { + const id = part.data?.id; + const name = part.data?.name; + const partData = partsData.find(pd => pd.id === id); + const accI = acc.findIndex(ad => ad.id === id); + const lastValue = acc[accI]; + const val = { cant: (lastValue?.cant ?? 0) + 1, data: partData?.data, id, name } as PartData; + AddOrEdit(acc, val, accI); + return acc; + }, [] as PartData[]); + const eventMustBeTriggered = newPartsData.length !== partsData.length + || newPartsData.some(npd => partsData.find(pd => pd.id === npd.id)?.cant !== npd.cant); + if(eventMustBeTriggered) + ChangePartsData(newPartsData); + }; + const ChangePartsData = (newPartsData: PartData[]) => { + setPartsData(newPartsData); + if(typeof onChangePartsData === 'function'){ + onChangePartsData(newPartsData); + } }; - /** * We memoize the keyword to know should we show mention suggestions or not */ @@ -86,6 +123,18 @@ const MentionInput: FC = ( if (!newValue) { return; } + // + const copyPartsData = [...partsData]; + const i = copyPartsData.findIndex(ad => ad.id === suggestion.id); + const lastValue = copyPartsData[i]; + const val = { + cant: (lastValue?.cant ?? 0) + 1, + data: suggestion, + id: suggestion.id, + name: suggestion.name + } as PartData; + AddOrEdit(copyPartsData, val, i); + ChangePartsData(copyPartsData); onChange(newValue); diff --git a/src/types/types.ts b/src/types/types.ts index 5ec7d99..1ba5599 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -13,6 +13,12 @@ type MentionData = { name: string; id: string; }; +type PartData = { + name: string; + id: string; + cant?: number; + data: any; +}; type CharactersDiffChange = Omit & { count: number }; @@ -93,8 +99,11 @@ type Part = { type MentionInputProps = Omit & { value: string; - onChange: (value: string) => any; - + onChange: (value: string) => void; + // returns an array with the data passed in the onSuggestionPress method in renderSuggestion. It allows you to overload with data in case you need more than the name and the id + onChangePartsData: (partsData: PartData[]) => void; + // Default value for partsData, useful when MentionInput is passed a value by the 'value' or 'defaultValue' property that the user has not entered. This property is only used as an initial value. + defaultPartsData: Omit[]; partTypes?: PartType[]; inputRef?: Ref; @@ -114,4 +123,5 @@ export type { PatternPartType, PartType, MentionInputProps, + PartData };