Skip to content
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

adding onChangePartsData event #43

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
#
.idea
node_modules/
dist
dist/
npm-debug.log
yarn-error.log
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
"url": "git+https://github.com/dabakovich/react-native-controlled-mentions.git"
},
"author": "David Tabaka <[email protected]>",
"contributors": [
"David Tabaka",
"Denifer Santiago"
],
"license": "MIT",
"devDependencies": {
"@types/diff": "^4.0.2",
Expand Down
63 changes: 56 additions & 7 deletions src/components/mention-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -16,6 +16,13 @@ import {
isMentionPartType,
parseValue,
} from '../utils';
const AddOrEdit = <S extends unknown>(arr: S[], val: S, i: number) => {
if(i === -1) {
arr.push(val);
} else {
arr[i] = val;
}
}

const MentionInput: FC<MentionInputProps> = (
{
Expand All @@ -27,15 +34,16 @@ const MentionInput: FC<MentionInputProps> = (
inputRef: propInputRef,

containerStyle,

defaultPartsData = [],
onSelectionChange,

onChangePartsData,
...textInputProps
},
} : MentionInputProps,
) => {
const textInput = useRef<TextInput | null>(null);

const [selection, setSelection] = useState({start: 0, end: 0});
const [partsData, setPartsData] = useState<PartData[]>(defaultPartsData);

const {
plainText,
Expand All @@ -54,9 +62,38 @@ const MentionInput: FC<MentionInputProps> = (
* @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
*/
Expand Down Expand Up @@ -86,6 +123,18 @@ const MentionInput: FC<MentionInputProps> = (
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);

Expand Down
14 changes: 12 additions & 2 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ type MentionData = {
name: string;
id: string;
};
type PartData = {
name: string;
id: string;
cant?: number;
data: any;
};

type CharactersDiffChange = Omit<Change, 'count'> & { count: number };

Expand Down Expand Up @@ -93,8 +99,11 @@ type Part = {

type MentionInputProps = Omit<TextInputProps, 'onChange'> & {
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<PartData, 'cant'>[];
partTypes?: PartType[];

inputRef?: Ref<TextInput>;
Expand All @@ -114,4 +123,5 @@ export type {
PatternPartType,
PartType,
MentionInputProps,
PartData
};