Simulate "shift" pressing key on checkbox to select multiple rows - javascript

I have the following input
<input type="checkbox" checked={isChecked}
onChange={handleOnChange}/>
and my function is this
const handleOnChange = () => {
let element:any = document.querySelector('input');
element.onkeydown = (e: { key: any; }) => alert(e.key);
element.dispatchEvent(new KeyboardEvent('keydown',{'key':'Shift'}));
setIsChecked(!isChecked);
};
This checkbox is created dinamically as I add new rows and I would like to simulate holding the key "shift" so that when I check multiple checkboxes these rows remain selected.
I am using reactjs.

There's no native way to do it but you can implement it based on the index you have checked.
const checkboxes = new Array(20).fill(null);
export default function App() {
const [checked, setChecked] = useState([]);
const lastChecked = useRef(null);
const handleChange = useCallback((e) => {
const index = Number(e.target.dataset.index);
if (lastChecked.current !== null && e.nativeEvent.shiftKey) {
setChecked((prev) => {
const start = Math.min(lastChecked.current, index);
const end = Math.max(lastChecked.current, index);
return uniq([...prev, ...range(start, end), end]);
});
return;
}
if (e.target.checked) {
lastChecked.current = index;
setChecked((prev) => [...prev, index]);
} else {
lastChecked.current = null;
setChecked((prev) => prev.filter((i) => i !== index));
}
}, []);
return (
<div>
{checkboxes.map((_, i) => (
<div key={i}>
<label>
<input
checked={checked.includes(i)}
data-index={i}
type="checkbox"
onChange={handleChange}
/>
checkbox {i}
</label>
</div>
))}
</div>
);
}

Related

How to set array of values to state and fetch during submit in React

Depending on the count, the number of text box will be generated.
When I try to set the text box value to the state, textbox is not accepting more than one value.
import { useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
const [option, setOption] = useState([]);
const Generator = (count) => {
let textArr = [];
for (let i = 0; i < count.count; i++) {
let name = "txt" + i;
textArr.push(<input type="text" key= {name} id={name} onChange={e => onChangeOptions(e,i)} value={getOptionValue(i)}/>);
}
return textArr;
};
const getOptionValue = (i) => {
let opt = [...option];
return opt[i];
}
const onChangeOptions = (e,i) => {
let val = e.target.value;
let opt = [...option];
opt[i] = val;
setOption(opt);
console.log(opt);
}
const onSubmitForm = (e) => {
console.log(option);
}
return (
<div>
<form onSubmit={onSubmitForm}>
<input
type="text"
id="count"
onChange={(e) => setCount(e.target.value)}
/>
<br />
<Generator count={count} />
<button>Submit</button>
</form>
</div>
);
}
Generator is defined inside your component, so it's a different function on each render. React thinks a different element is being rendered each time, which makes the input lose focus. Instead, directly create the array of inputs in the JSX with Array#map:
{
[...Array(+count)].map((_, i) => (
<input type="text" key={"txt" + i} id={"txt" + i} onChange={e => onChangeOptions(e,i)} value={getOptionValue(i)}/>
))
}
Alternatively, extract Generator into a separate component. Do not define it inside another component.

Modifying Hook setState for an Array in React using Splice

I'm trying to splice an array hook State, so I can remove items from my list. I know you're not supposed to modify any hook states directly, so I made a copy into array2 before settting it, but it's removing all items below the one I click, instead of just the one I click. Any ideas?
export default function Shopping() {
const [item, setItem] = useState('Default');
const [list, setList] = useState([]);
const [array, setArray] = useState([]);
let array2 = [];
const name = ({ target }) => setItem(target.value);
const remove = ({ target }) => {
for (let x = 0; x <= array.length; x++) {
if (array[x] === target.value) {
array2 = list;
array2 = array2.splice(x, 1);
}
setList([...list, array2]);
}
};
const create = () => {
if (item !== 'Default' && document.getElementById('box').value !== '') {
let value = document.getElementById('box').value;
setArray([...array, value]);
setList([
...list,
<div>
<p>
{' '}
{item} Qty: 1<button>+</button>
<button>-</button>
<button
value={document.getElementById('box').value}
onClick={remove}
>
x
</button>
</p>
</div>,
]);
} else {
alert('Please enter an Item');
}
document.getElementById('box').value = '';
setItem('Default');
};
const clear = () => setList([]);
return (
<div>
<input type='textbox' id='box' onChange={name} />
<input type='button' value='Add' onClick={create} />
<input type='button' value='Clear' onClick={clear} />
{list}
</div>
);
}
You should just store the strings in the list array and display the elements using a map function:
export default function Shopping() {
const [item, setItem] = useState('Default');
const [list, setList] = useState([]);
const name = ({ target }) => setItem(target.value);
const remove = (index) => {
const filteredList = [...list].splice(index, 1);
setList(filteredList);
};
const create = () => {
if (item !== 'Default' && item !== '') {
setList([...list, item]);
} else {
alert('Please enter an Item');
}
setItem('Default');
};
const clear = () => setList([]);
return (
<div>
<input type='textbox' id='box' onChange={name} />
<input type='button' value='Add' onClick={() => create()} />
<input type='button' value='Clear' onClick={() => clear()} />
{list.map((it, index) => {
return (
<div key={index}>
<p>
{' '}
{it} Qty: 1<button>+</button>
<button>-</button>
<button value={it} onClick={() => remove(index)}>
x
</button>
</p>
</div>
);
})}
</div>
);
}
```
Just Update the code in remove function because array2 = list also copies the reference of list array so when you update the array2 it also update the list array.so replace the remove function code with given code below .
if ((array[x])===target.value){
array2 = list.map(item=>item)
array2 = array2.splice(x,1)
}

Add autosuggest to TagsInput

To write the site, I use JavaScript, the React framework, and the library mui.
On my site, one of the input fields for the user is done using TagsInput. Thus, the user can enter data, press enter, see the tag, and optionally delete it.
For TagsInput, I did not use libraries, but wrote the code myself. It will be presented below.
I would like to improve the functionality for the user and add a predictive search (autofill). That is, when the user begins to enter letters (let it be, for example, car brands), if the car brand is currently in the database (not to complicate things, you can make a regular list or other convenient type of data), the user will be offered options for autofill.
For example: the user clicks on the field for entering a tag and enters the first letter "A" and prompts are given to him - Alfa Romeo, Aston Martin. If the Audi is not currently in the database, then the Audi tooltip will not pop up.
I will be glad for any help.
FilterMarkAuto.jsx
export default function FilterMarkAuto({ isExpanded, setIsExpanded }){
const [values, setValues] = useState([]);
return (
<ArrowDropdown
isExpanded={isExpanded}
setIsExpanded={setIsExpanded}
title="Name car"
onClick={() => setIsExpanded(!isExpanded)}>
{isExpanded &&
<TagsInput tags={values}
setTags={setValues}
inputPlaceholder="Enter a car" />}
</ArrowDropdown>
);
}
TagsInput.jsx
export default function TagsInput(props) {
const tags = props.tags
const setTags = props.setTags
const [input, setInput] = React.useState('');
const [isKeyReleased, setIsKeyReleased] = React.useState(false);
const onChange = (e) => {
const { value } = e.target;
setInput(value);
};
const onKeyDown = (e) => {
const { key } = e;
const trimmedInput = input.trim();
if ((key === ',' || key === 'Enter') && trimmedInput.length && !tags.includes(trimmedInput)) {
e.preventDefault();
setTags(prevState => [...prevState, trimmedInput]);
setInput('');
}
if (key === "Backspace" && !input.length && tags.length && isKeyReleased) {
e.preventDefault();
const tagsCopy = [...tags];
const poppedTag = tagsCopy.pop();
setTags(tagsCopy);
setInput(poppedTag);
setIsKeyReleased(false);
}
};
const onKeyUp = () => {
setIsKeyReleased(true);
}
const deleteTag = (index) => {
setTags(prevState => prevState.filter((tag, i) => i !== index))
}
return (
<div className={classes.container}>
{tags.map((tag, index) => <div className={classes.tag}>
<ClearIcon className={classes.del} fontSize="big" onClick={() => deleteTag(index)} />
{tag}
</div>
)}
<input
className={classes.input}
value={input}
placeholder={props.inputPlaceholder}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
onChange={onChange}
/>
</div>);
}
Here you go: https://codesandbox.io/s/beautiful-albattani-kn1yfr
I added the following tags as suggestions:
const tagSuggestions = [
"Audi",
"Mercedes Benz",
"Renault",
"Ford",
"Ferrari"
]
There isn't much to it, this is the part that renders the suggestions:
{suggestedTags.length > 0 && (
<div className={classes.tagSuggestionWrapper}>
{suggestedTags.map((t) => {
return (<div key={t} className={classes.tagSuggestion} onClick={() => { selectTag(t) }}>{t}</div>);
})}
</div>
)}
And then there's the tag selection from suggestions:
const selectTag = (tag) => {
setTags((prevState) => [...prevState, tag]);
setSuggestedTags([]);
setInput("");
}
And the search of the suggestions based on what has been typed:
const onChange = (e) => {
const { value } = e.target;
setInput(value);
if(value.length < 2) return;
const matchedSuggestions = tagSuggestions.filter((s) => {
return s.toLowerCase().search(value.toLowerCase()) > -1;
})
setSuggestedTags(matchedSuggestions);
};

TODOLIST Update in REACT

So I was trying to update the value I got by the Addlist and I tried this but this isn;t working. Also when I click on the '+' button without writing anything, an empty list is created. How should I stop it. I've attached a code below.
import React from "react";
import "./App.css";
import { useState } from "react";
import TodoList from "./components/TodoList";
function App() {
const [input, setInput] = useState("");
const [list, setList] = useState([]);
const updateList = (e) => {
setInput(e.target.value);
};
const AddList = () => {
console.log("value added")
setList((addValue) => {
return [...addValue, input];
});
setInput("");
};
const updateItems=(id)=>{
const newValue=[...list].map((newVal)=>{
if(input.id===id){
input.text='';
}
return newVal;
})
setList(newValue);
}
const deleteItems = (id) => {
console.log("deleted");
setList((addValue) => {
return addValue.filter((element, index) => {
return index !== id;
});
});
};
return (
<div className="todo-app">
<h1> Enter Anything</h1>
<input
type="text"
placeholder="Add anything"
value={input}
onChange={updateList}
/>
<button onClick={AddList}>+</button>
<ul>
{list.map((itemsvalue, id) => {
return (
<TodoList
itemsValue={itemsvalue}
key={id}
onSelect={deleteItems}
id={id}
onUpdate={updateItems}
/>
);
})}
</ul>
</div>
);
}
export default App;
Any kind of help would be appreciated. Also if I want to split this into multiple components is there a way to do.
When user clicks on the add button there is the check for empty String AddList method
for ex:- User updates second index value, second position value will get updated.
const [input, setInput] = useState('');
const [list, setList] = useState([]);
const [index, setIndex] = useState(null);
const updateList = (e) => {
setInput(e.target.value);
};
useEffect(() => {
setList(list);
console.log(list, '<>?');
}, [index]);
const AddList = () => {
if (input.trim() !== '') {
setList([...list, input]);
}
setInput('');
};
const updateValue = (index) => {
console.log(list[index]);
setIndex(index);
if (list[index].trim() !== '') {
setInput(list[index]);
}
};
const UpdateList = () => {
list[index] = input;
console.log(list, 'before <>?');
setIndex(null);
setInput('');
};
return (
<div>
<input type="text" placeholder="Add anything" value={input} onChange={updateList} />
<button disabled={!index && !list.length === 0} onClick={AddList}>
Add
</button>
<button disabled={input.trim() === ''} onClick={UpdateList}>
Update
</button>
{list.map((m, index) => (
<h1 style={{ border: '1px solid black' }} onClick={() => updateValue(index)}>
{m}
</h1>
))}
</div>
);

Disable all checkboxes within a array in a row

I am trying to disable a set of checkbox within a row. For example,
I have the following
{availableBendsHostnames?.map((bEnds, indx) => (
<div key={indx}>
<div>B-End: {bEnds}</div>
<div>
<div>
<div>
{responseMessage !== 'success' &&
listOfEvenIpsWithCorrectSubnet?.map((ipAddress, idx) => (
<div key={idx}>
<FormControlLabel
control={
<Checkbox
name={`ip-${ipAddress}-${indx}`}
id={`ip-${ipAddress}-${indx}`}
value={ipAddress}
disabled={!isChecked[idx] && isDisabled}
onChange={(e) => handleIPSelected(e, idx, indx)}
/>
}
label={ipAddress}
/>
</div>
))}
</div>
</div>
</div>
</div>
))}
this out put as the following
row a
checkbox0 (name = ip-11.0.0.16/31-0, index = ip-11.0.0.16/31-0),
checkbox1 (name = ip-11.0.0.18/31-0, index = ip-11.0.0.18/31-0),
checkbox2 (name = ip-11.0.0.20/31-0, index = ip-11.0.0.20/31-0)
row b
checkbox0 (name = ip-11.0.0.16/31-1, index = ip-11.0.0.16/31-1),
checkbox1 (name = ip-11.0.0.18/31-1, index = ip-11.0.0.18/31-1),
checkbox2 (name = ip-11.0.0.20/31-1, index = ip-11.0.0.20/31-1)
row b
checkbox0 (name = ip-11.0.0.16/31-2, index = ip-11.0.0.16/31-2),
checkbox1 (name = ip-11.0.0.18/31-2, index = ip-11.0.0.18/31-2),
checkbox2 (name = ip-11.0.0.20/31-2, index = ip-11.0.0.20/31-2)
What I would like to happen is when the user checks on
checkbox0 (name = ip-11.0.0.16/31-0, index = ip-11.0.0.16/31-0)
I would like to disable the other checkbox in that row.
Here is what I have so far,
in my handle selected method I have the following
const [isChecked, setIsChecked] = useState({ checked: {} });
const [isDisabled, setIsDisabled] = useState();
const handleIPSelected = (selectIPIndx,) => {
setIsChecked((previousState) => ({
checked: {
...previousState.checked,
[selectIPIndx]: !previousState.checked[selectIPIndx],
},
}));
}
And I have a useEffect
useEffect(() => {
const checkedCount = Object.keys(isChecked).filter(
(key) => isChecked[key] && Object.keys(isChecked[key]).length !== 0
).length;
const disabled = checkedCount > 0;
setIsDisabled(disabled);
}, [isChecked]);
At the moment with this code as soon as I click on one checkbox it disables all checkboxes not just the one in the row. Any help would be appreciated.
You need to save for each checked item it's row.
There are also small errors, I fixed them to make it work:
const [isChecked, setIsChecked] = useState({});
const [disabledRows, setDisabledRows] = useState([]);
useEffect(() => {
const disabledRows = Object.keys(isChecked)
.filter((key) => isChecked[key].checked)
.map((key) => isChecked[key].row);
setDisabledRows(disabledRows);
}, [isChecked]);
const responseMessage = "error";
const availableBendsHostnames = [1, 2, 3];
const listOfEvenIpsWithCorrectSubnet = [4, 5, 6];
const handleIPSelected = (e, idx, row) => {
const { id } = e.currentTarget;
setIsChecked((previousState) => ({
...previousState,
[id]: {
checked: !previousState[id]?.checked,
row
}
}));
};
You can refer to this Code Sandbox working example

Categories