Handle multiple actions in handleBeforeInput for DraftJS - javascript

I am in a fix in this situation. If the user presses '1. ', i change the block to ordered block. Following is the code I do to change it:
_handleBeforeInput(str) {
if (str !== '.') {
return false;
}
const { editorState } = this.state;
const selection = editorState.getSelection();
const currentBlock = editorState.getCurrentContent()
.getBlockForKey(selection.getStartKey());
const blockLength = currentBlock.getLength();
if (blockLength === 1 && currentBlock.getText() === '1') {
this.onChange((utilFn.resetBlockType(editorState, 'ordered-list-item')));
return 'handled';
}
return 'not-handled';
}
However, once the user creates an ordered-list-item block, I want to set a limit on the block being created. I tried to use the answer from this question: [How to limit Max Length of Draft js, however I dont know how can i handle multiple handlers in handlebeforeInput.
I tried using switch case etc. but it wasnt helping.
Please help me with this issue if anyone has faced it. Thanks!

I realized my mistake... it is pretty straightforward with using multiple if-else, following is the adjusted code:
_handleBeforeInput(str) {
const { editorState } = this.state;
const selection = editorState.getSelection();
const currentBlock = editorState.getCurrentContent()
.getBlockForKey(selection.getStartKey());
const blockLength = currentBlock.getLength()
const currentContent = this.state.editorState.getCurrentContent();
const currentContentLength = currentContent.getPlainText('').length
if (currentContentLength > 10 - 1){
alert('you can type max ten characters');
return 'handled';
}else if (str !== '.') {
return 'not-handled';
}else if(str === '.'){
if (blockLength === 1 && currentBlock.getText() === '1') {
this.onChange((utilFn.resetBlockType(editorState, 'ordered-list-item')));
return 'handled';
}
}
return 'not-handled';
}

Related

Replacing conditional chaining with an older method

I have some code that is using conditional chaining such as
productQuantity = purchases.filter((pObj) => pObj.elements[0]?.prodType?.id === purchase.elements[0]?.prodType?.id && pObj.sold === purchase.sold).length
It works fine but I need to convert the chained conditionals to an older method and I'm not sure how. Can anyone advise ?
Reason being that PM2 does not support chaining conditional operators.
Let's try this. Replacing all key?.key2 with key && key.key2
productQuantity = purchases.filter((pObj) => {
return pObj.elements[0] && pObj.elements[0].prodType && purchase.elements[0] && purchase.elements[0].prodType && pObj.elements[0].prodType.id === purchase.elements[0].prodType.id && pObj.sold === purchase.sold
}).length
I would probably do something like this.
First, we make the observation that the we are selecting an id from two similar objects: we can therefore refactor the logic required to select the id into a common function:
function selectId(item) {
if (item) {
const elements = item.elements;
if (elements) {
const element = elements[0];
if (element) {
const prodType = element.prodType;
if (prodType) {
const id = prodType.id;
return id;
}
}
}
}
}
You could also flatten the selection (which may or may not be more readable/maintainable):
function selectId(item) {
if (!item) return undefined;
const elements = item.elements;
if (!elements) return undefined;
const element = elements[0];
if (!element) return undefined;
const prodType = element.prodType;
if (!element) return undefined;
const id = prodType.id;
return id;
}
Once you have that, then your filter comes down to this:
productQuantity = purchases.filter( isMatch ).length;
function isMatch(obj) {
let itemId = selectId(obj);
let purchaseId = selectId(purchase);
const shouldKeep = itemId == purchaseId
&& obj.sold === purchase.sold;
return shouldKeep
}

Why does this my recursive function work after dividing a tag.class selector without dividing the matching tag.class new one?

I've been trying to do some html and css but I'm really bad at this so far. I was using a function that would check if two selectors match. A friend of mine came up with this code but neither of us fully understands how the return of the "tag.class" case works. My question is, if it doesn't divide the newSelector, how can it succesfully check the tag and class?
var matchFunctionMaker = function(selector) {
var selectorType = selectorTypeMatcher(selector);
var matchFunction;
if (selectorType === "id") {
matchFunction = nuevoSelector => '#' + nuevoSelector.id === selector;
} else if (selectorType === "class") {
matchFunction = nuevoSelector => {
var lista = nuevoSelector.classList;
for (let x of lista) {
if (selector === '.' + x) return true;
}
return false;
}
} else if (selectorType === "tag.class") {
matchFunction = nuevoSelector => {
var [tag, clase] = selector.split('.');
return matchFunctionMaker(tag) (nuevoSelector) && matchFunctionMaker(`.${clase}`) (nuevoSelector);
};
} else if (selectorType === "tag") {
matchFunction = nuevoSelector => nuevoSelector.tagName.toLowerCase() === selector.toLowerCase();
}
return matchFunction;
};
Thanks in advance!

.forEach validates each element but not the whole input? JS

I'm trying to validate a form input with multiple hashtags. I absolutely have to split the input and convert it to lowerCase.
ex) #sun #sea #summer #salt #sand
When I'm typing a hashtag that fails the validation, bubble message pops up and tells me it's wrong.
But if I type the next hashtag correctly, previous bubble message clears and the whole validation fails (form can be sent).
I'm assuming it has something to do with .forEach – possibly there are better solutions I'm not yet aware of.
I'm new to JS and would appreciate your answers very much.
// conditions for validation
const hashtagInput = document.querySelector('.text__hashtags');
const commentInput = document.querySelector('.text__description');
const testStartWith = (hashtag) => {
if (!hashtag.startsWith('#')) {
return 'hashtag should start with #';
}
return undefined;
};
const testShortValueLength = (hashtag) => {
if (hashtag.length === 1) {
return 'hashtag should have something after #';
}
return undefined;
};
const testValidity = (hashtag) => {
const regex = /^[A-Za-z0-9]+$/;
const isValid = regex.test(hashtag.split('#')[1]);
if (!isValid) {
return 'hashtag can't have spaces, symbols like #, #, $, etc, or punctuation marks';
}
return undefined;
};
const testLongValueLength = (hashtag) => {
if (hashtag.length > 20) {
return 'maximum hashtag length is 20 symbols';
}
return undefined;
};
const testUniqueName = (hashtagArray, index) => {
if (hashtagArray[index - 1] === hashtagArray[index]) {
return 'the same hashtag can't be used twice';
}
return undefined;
};
const testHashtagQuantity = (hashtagArray) => {
if (hashtagArray.length > 5) {
return 'only 5 hashtags for each photo';
}
return undefined;
};
const testCommentLength = (commentInput) => {
if (commentInput.value.length >= 140) {
return 'maximum comment length is 140 symbols';
}
return undefined;
};
const highlightErrorBackground = (element) => {
element.style.backgroundColor = '#FFDBDB';
};
const whitenBackground = (element) => {
element.style.backgroundColor = 'white';
};
Here is the validation at work
const testHashtagInput = () => {
const hashtagArray = hashtagInput.value.toLowerCase().split(' ');
hashtagArray.forEach((hashtag, index) => {
let error = testStartWith(hashtag)
|| testShortValueLength(hashtag)
|| testValidity(hashtag)
|| testLongValueLength(hashtag)
|| testUniqueName(hashtagArray, index)
|| testHashtagQuantity(hashtagArray);
if (error) {
highlightErrorBackground(hashtagInput);
hashtagInput.setCustomValidity(error);
} else {
whitenBackground(hashtagInput);
hashtagInput.setCustomValidity('');
}
hashtagInput.reportValidity();
});
if (hashtagInput.value === '') {
whitenBackground(hashtagInput);
hashtagInput.setCustomValidity('');
}
hashtagInput.reportValidity();
};
const testCommentInput = () => {
let error = testCommentLength(commentInput);
if (error) {
highlightErrorBackground(commentInput);
commentInput.setCustomValidity(error);
} else {
whitenBackground(commentInput);
commentInput.setCustomValidity('');
}
commentInput.reportValidity();
};
hashtagInput.addEventListener('input', testHashtagInput);
Yes your forEach reevaluates the entire validity of the input based on the individual items but only the last one actually remains in the end because it's the last one being evaluated.
You could change your evaluation to an array-reducer function; best fit for your intention is the reducer "some" (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some).
It iterates over the list of items and returns whether any one (or more) of the items fullfills the criterion formulated in your callback function.
I didn't get to test it now, but I guess this should do it:
let error = hashtagArray.some((hashtag, index) => {
return (testStartWith(hashtag)
|| testShortValueLength(hashtag)
|| testValidity(hashtag)
|| testLongValueLength(hashtag)
|| testUniqueName(hashtagArray, index)
|| testHashtagQuantity(hashtagArray));
});
if (error) {
highlightErrorBackground(hashtagInput);
hashtagInput.setCustomValidity(error);
} else {
whitenBackground(hashtagInput);
hashtagInput.setCustomValidity('');
}
hashtagInput.reportValidity();
HTH, cheers

console.log and arithmatic operations does not work inside (addEventListener) javascript

i try to create typing test web application //error ///
this code i copied from github https://github.com/WebDevSimplified/JS-Speed-Typing-Game
i try to add timer when user press a key..
var err=0;
let sttime =0;
console.log(sttime);
quoteInputElement.addEventListener('input', () => {
err ++; /////does not work
console.log(sttime); ///does not work
console.log('jbjabj');
const arrayQuote = quoteDisplayElement.querySelectorAll('span');
const arrayValue = quoteInputElement.value.split('');
let correct = true;
arrayQuote.forEach((characterSpan, index) => {
const character = arrayValue[index];
if (character == null) {
characterSpan.classList.remove('correct');
characterSpan.classList.remove('incorrect');
correct = false
} else if (character === characterSpan.innerText) {
characterSpan.classList.add('correct');
characterSpan.classList.remove('incorrect');
} else {
characterSpan.classList.remove('correct');
characterSpan.classList.add('incorrect');
correct = false;
}
})
})
console.log(sttime);
if(sttime == 1){
startTimer();
}

Making "Fill in the Blank" in react

I am quite new to react and I am trying to make a fill in the blank app with react. Basically, I have a passage with a word list. I want to replace all occurrences each word with a blank so that the user can type in the answer. After that, if the user clicks the submit button, it displays the result saying how many they got right.
After doing some research, I found reactStringReplace package which can safely replace strings with react components. This is how I generate the blanks in the passage:
getFillInTheBlank() {
let passage = this.passage;
for (var i = 0; i < this.wordList.length; i++) {
let regexp = new RegExp("\\b(" + this.wordList[i] + ")\\b", "gi");
passage = reactStringReplace(passage, regexp, (match, i) => (
<input type="text"></input>
));
}
return <div>passage</div>
}
However, I can't figure out a way to check each input text with respective words to calculate the score when the submit button is clicked. Can anyone suggest a way of doing this? Thank you in advance.
I made it using Mobx, but it can be easily edited to work without this library.
This is the model, which contains the word to guess and the main events callbacks
word-guess.tsx
import { makeAutoObservable } from "mobx";
export class WordGuess {
private wordToGuess: string;
// Needed to select the next empty char when the component gain focus
private nextEmptyCharIndex = 0;
guessedChars: string[];
focusedCharIndex = -1;
constructor(wordToGuess: string) {
this.wordToGuess = wordToGuess;
// In "guessedChars" all chars except white spaces are replaced with empty strings
this.guessedChars = wordToGuess.split('').map(char => char === ' ' ? char : '');
makeAutoObservable(this);
}
onCharInput = (input: string) => {
this.guessedChars[this.focusedCharIndex] = input;
this.focusedCharIndex += 1;
if(this.nextEmptyCharIndex < this.focusedCharIndex){
this.nextEmptyCharIndex = this.focusedCharIndex;
}
};
onFocus = () => this.focusedCharIndex =
this.nextEmptyCharIndex >= this.wordToGuess.length ? 0 : this.nextEmptyCharIndex;
onFocusLost = () => this.focusedCharIndex = -1;
}
Input Component
guess-input.tsx
interface GuessInputProps {
wordGuess: WordGuess;
}
export const GuessInput = observer((props: GuessInputProps) => {
const { guessedChars, focusedCharIndex, onCharInput, onFocus, onFocusLost } =
props.wordGuess;
const containerRef = useRef(null);
const onClick = useCallback(() => {
const ref: any = containerRef?.current;
ref?.focus();
}, []);
useEffect(() => {
const onKeyDown = (params: KeyboardEvent) => {
const key = params.key;
if (focusedCharIndex >= 0 && key.length === 1 && key.match(/[A-zÀ-ú]/)) {
onCharInput(params.key);
// Clear focus when last character is inserted
if(focusedCharIndex === guessedChars.length - 1) {
const ref: any = containerRef?.current;
ref?.blur();
}
}
};
document.addEventListener('keydown', onKeyDown);
return () => {
document.removeEventListener('keydown', onKeyDown);
};
}, [focusedCharIndex, guessedChars]);
return <div className='guess-input'
onClick={onClick} ref={containerRef}
onFocus={onFocus} onBlur={onFocusLost} tabIndex={-1}>
{guessedChars.map((char, index) =>
<CharToGuess key={index} value={char} focused={index === focusedCharIndex} />)
}
</div>;
});
Component representing each one of the characters
char-to-guess.tsx
import './guess-input.scss';
interface CharToGuessProps {
value: string;
focused: boolean;
}
export const CharToGuess = (props: CharToGuessProps) => {
const { focused, value } = props;
return <span className={`char-to-guess ${focused ? ' focused-char' : ''}`}>
{value || '_'}
</span>;
};
You don't want to create blank strings.
Take a look at this code and see if you understand it.
var answer = document.getElementById('guess-input').name;
var hint = document.getElementById('guess-input').value;
function guessAnswer() {
$("button.guess-submit").click(function(event) {
var guess = $('#guess-input').val();
guess = guess.toLowerCase();
if ( guess == answer) {
$('#correct').show();
$('#wrong').hide();
} else {
$('#wrong').show().fadeOut(1000);
$('#guess-input').val(hint);
}
});
}
function enterSubmit() {
$("#guess-input").keyup(function(event){
if(event.keyCode == 13){
$("#guess-submit").click();
}
});
guessAnswer();
}
enterSubmit();
if ( $('#correct').css('display') == 'block') {
alert('hi');
}
I suggest to send a request to server with the questions and answers and return the results. If you save the points or the answers in the frontend, is possible that the game will be altered.

Categories