I'm trying to achieve selecting just only one row at a time in my React Material App. My problem now is that it is highlighting many rows. I only need to highlight/select one row at a time.
Pls check this codesandbox link
CLICK HERE
CODE
const [selected, setSelected] = React.useState([]);
const selectFood = (event, food) => {
const selectedIndex = selected.indexOf(food.name);
let newSelected = [];
if (selectedIndex === -1) {
newSelected = newSelected.concat(selected, food.name);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selected.slice(1));
} else if (selectedIndex === selected.length - 1) {
newSelected = newSelected.concat(selected.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(
selected.slice(0, selectedIndex),
selected.slice(selectedIndex + 1)
);
}
setSelected(newSelected);
};
const isSelected = row => selected.indexOf(row.name) !== -1;
If you want to just select a single row -
then this would work definitely ( no need of complex code which you have written )
const selectFood = (event, food) => {
let newSelected = [food.name];
setSelected(newSelected);
};
Working link - https://codesandbox.io/s/highlightselect-rows-react-material-8ejbc?file=/demo.js:1016-1123
Related
So I am trying to building a simple OPT forum which I have build but now I want to add reverse functionality for example when user press backspace input should focus on previous element.
const inputs = document.querySelectorAll('.input input')
inputs.forEach((e, i) => {
e.dataset.index = i;
e.addEventListener("input", () => {
if (e.value.length >= 1) {
if (e.dataset.index >= i && inputs.length - 1 > i) {
e.nextElementSibling.focus();
}
} else {
if (inputs.length - 1 > i || e.dataset.index < inputs.length) {
e.previousElementSibling.focus();
}
}
})
})
You would have to detect the press of the backspace key.
It looks like you want each input to take exactly one character. In that case I would suggest that you keep the current character selected, and to make arrow keys also move the focus.
Don't name the input element e, as e is usually used as name for the event object -- just to avoid confusion for someone reading your code.
Here is one way to do it:
const inputs = [...document.querySelectorAll('.input input')];
inputs.forEach((input, i) => {
const previous = () => inputs[i-1]?.focus?.();
const next = () => inputs[i+1]?.focus?.();
input.addEventListener("input", () => {
if (input.value.length > 1) input.value = input.value[0];
if (input.value.length) next();
});
input.addEventListener("keydown", (e) => {
if (e.key == "ArrowRight") next();
if (e.key == "ArrowLeft") previous();
});
input.addEventListener("keyup", (e) => {
if (e.key == "Backspace") {
previous();
e.preventDefault();
}
});
});
inputs[0].focus();
// Ensure that current input has its content selected
document.addEventListener("selectionchange", () => {
const input = document.activeElement;
if (inputs.includes(input)) input.setSelectionRange(0, 1);
});
.input input { width: 1em }
<div class="input">
<input><input><input><input><input>
</div>
I'm learning JavaScript, and I've done this code, I just want to be sure that I'm on the right way, would you have done it differently?
What bothers me is especially to have passed parameters to the functions "next" and "updateArrows" to know the origin of the clicks
const changeStep = document.querySelectorAll(".step");
const currentPaginate = document.querySelector(".pagination span.active");
const arrows = document.querySelectorAll(".arrow");
for (let arrow of arrows) {
arrow.addEventListener("click", function () {
updateArrows(arrow);
});
}
for (let step of changeStep) {
step.addEventListener("click", function () {
next(step);
});
}
function updateArrows(arrow, currentStep = null, update = true) {
let nextStep;
if (currentStep == null) {
currentStep = document.querySelector(".step.current");
if (arrow.classList.contains("arrow-bottom")) {
nextStep = currentStep.nextElementSibling;
} else {
nextStep = currentStep.previousElementSibling;
}
} else nextStep = document.querySelector(".step.current");
if (!arrow.classList.contains("impossible")) {
if (nextStep.dataset.id != 1 && nextStep.dataset.id != 5) {
arrows.forEach(function (arrow) {
if (arrow.classList.contains("impossible")) {
arrow.classList.remove("impossible");
}
});
} else if (nextStep.dataset.id == 5) {
if (arrow.previousElementSibling.classList.contains("impossible"))
arrow.previousElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
} else if (nextStep.dataset.id == 1) {
if (arrow.nextElementSibling.classList.contains("impossible"))
arrow.nextElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
}
if (update == true) next(nextStep, false);
}
}
function next(step, update = true) {
if (!step.classList.contains("current")) {
const currentStep = document.querySelector(".step.current");
const nextStep = step.dataset.id;
currentStep.classList.remove("current");
step.classList.add("current");
currentPaginate.textContent = "0" + nextStep;
let arrow;
if (currentStep.dataset.id < nextStep) {
arrow = document.querySelector(".arrow-bottom");
} else {
arrow = document.querySelector(".arrow-top");
}
if (update == true) updateArrows(arrow, currentStep, false);
}
}
I see what you mean.
Yes you are right. You can do it better...
Instead of passing the parameter arrow you can read from an event object
const changeStep = document.querySelectorAll(".step");
const currentPaginate = document.querySelector(".pagination span.active");
const arrows = document.querySelectorAll(".arrow");
for (let arrow of arrows) arrow.addEventListener("click", updateArrows);
for (let step of changeStep) step.addEventListener("click", next);
function updateArrows(e, currentStep = null, update = true) {
let arrow = null
e.target ? arrow=e.target : arrow=e
let nextStep;
if (currentStep == null) {
currentStep = document.querySelector(".step.current");
if (arrow.classList.contains("arrow-bottom")) {
nextStep = currentStep.nextElementSibling;
} else {
nextStep = currentStep.previousElementSibling;
}
} else nextStep = document.querySelector(".step.current");
if (!arrow.classList.contains("impossible")) {
if (nextStep.dataset.id != 1 && nextStep.dataset.id != 5) {
arrows.forEach(function (arrow) {
if (arrow.classList.contains("impossible")) {
arrow.classList.remove("impossible");
}
});
} else if (nextStep.dataset.id == 5) {
if (arrow.previousElementSibling.classList.contains("impossible"))
arrow.previousElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
} else if (nextStep.dataset.id == 1) {
if (arrow.nextElementSibling.classList.contains("impossible"))
arrow.nextElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
}
if (update == true) next(nextStep, false);
}
}
function next(e, update = true) {
let step = null
e.target ? step = e.target : step=e
if (!step.classList.contains("current")) {
const currentStep = document.querySelector(".step.current");
const nextStep = step.dataset.id;
currentStep.classList.remove("current");
step.classList.add("current");
currentPaginate.textContent = "0" + nextStep;
let arrow;
if (currentStep.dataset.id < nextStep) {
arrow = document.querySelector(".arrow-bottom");
} else {
arrow = document.querySelector(".arrow-top");
}
if (update == true) updateArrows(arrow, currentStep, false);
}
}
This should work, if not please contact me beforehand.
While activating the eventListener an event object is passed to function and e.target is an element which was clicked in this case.
What I did was crucial because you sometimes call this function from an eventListener and sometimes from a code. If the element has e.target then it's from an eventListener and if not then it's from code.
Didn't have a chance to test it since I don't have the rest of the code. Let me know if it works.
Problem: Default checked radio button value shows on app, but if I change the tip value, then unselect and reselect a checkbox, it goes back to the original checked radio button value instead of changing to the new tip value.
Basicaly, my default radio checked button value is set to $5. If I select a checkbox option, then I change the tip value to $2 then unselect a checkbox, then reselect a checkbox, it will show the tip value as $5 instead of the new option $2
Here's my codepen. I added instructions on the codepen so you can see my problem
https://codepen.io/jaysomo10/pen/dydaKwZ
Here's my JS
window.menuItems = 0;
window.tips = 0;
const foodTotal = document.getElementById("price");
const tipTotal = document.getElementById("tip");
const orderTotal = document.getElementById("total");
let tipVal = document.querySelector(".tips:checked").value;
tipTotal.textContent = "$" + (tipVal / 100).toFixed(2);
document.addEventListener("click", ({ target }) => {
if (target.className === "food" && target.checked && tipVal) {
window.menuItems += parseInt(target.value);
window.tips = tipVal;
} else if (target.className === "food" && !target.checked) {
window.menuItems -= parseInt(target.value);
} else if (target.className === "tips" && target.checked) {
window.tips = parseInt(target.value);
} else {
return;
}
foodTotal.textContent = `$${(window.menuItems / 100).toFixed(2)}`;
tipTotal.textContent = `$${(window.tips / 100).toFixed(2)}`;
orderTotal.textContent = `$${(
(Number(window.menuItems) + Number(window.tips)) /
100
).toFixed(2)}`;
});
I can't seem to convert the logic to make the tip value to change after I unselect & re select a checkbox option.
tipVal is defined outside the click event. So it has always the value at the sart. You have to changhe it also inside the event
window.menuItems = 0;
window.tips = 0;
const foodTotal = document.getElementById("price");
const tipTotal = document.getElementById("tip");
const orderTotal = document.getElementById("total");
let tipVal = document.querySelector(".tips:checked").value;
tipTotal.textContent = "$" + (tipVal / 100).toFixed(2);
document.addEventListener("click", ({ target }) => {
tipVal = document.querySelector(".tips:checked").value;
if (target.className === "food" && target.checked && tipVal) {
window.menuItems += parseInt(target.value);
window.tips = tipVal;
} else if (target.className === "food" && !target.checked) {
window.menuItems -= parseInt(target.value);
} else if (target.className === "tips" && target.checked) {
window.tips = parseInt(target.value);
} else {
return;
}
foodTotal.textContent = `$${(window.menuItems / 100).toFixed(2)}`;
tipTotal.textContent = `$${(window.tips / 100).toFixed(2)}`;
orderTotal.textContent = `$${(
(Number(window.menuItems) + Number(window.tips)) /
100
).toFixed(2)}`;
});
remove this from your first if statement
if (target.className === "food" && target.checked && tipVal) {
window.menuItems += parseInt(target.value);
//window.tips = tipVal; // remove this
}
Nested flow
I need some help design nested multistep form. I have implemented it with a naive solution for tracking the step number, looking for a way to abstract and improve the architecture.
Currently, I have two states the currentStep and the currentSubStep. These are the handlers for back and continue.
const handleNext = () => {
const subSteps = steps?.[currentStep]?.subSteps;
if (currentStep === steps?.length - 1) {
return;
}
if (currentSubStep === subSteps?.length - 1) {
// move to the next step
setCurrentStep((prev) => prev + 1);
setCurrentSubStep(0);
} else {
// move to the next
setCurrentSubStep((prev) => prev + 1);
}
};
const handleBack = () => {
const subSteps = steps?.[currentStep]?.subSteps;
if (currentStep === 0) {
if (currentSubStep !== 0) {
setCurrentSubStep((prev) => prev - 1);
} else if (currentSubStep === 0) {
setCurrentSubStep(0);
} else {
setCurrentStep((prev) => prev - 1);
}
} else {
if (currentSubStep !== 0) {
setCurrentSubStep((prev) => prev - 1);
} else {
setCurrentStep((prev) => prev - 1);
setCurrentSubStep(subSteps?.length - 1 || 0);
}
}
};
Front end code
const [customerList, setCustomerList] = useState([]); //store all that information of the database in a list
//make an axios request to get information from database
useEffect(() => {
Axios.get("http://localhost:3001/customers").then((response) => {
setCustomerList(response.data);
});
}, []);
//sort function final
const [counter, setCounter] = useState(0);
const [sortedCustomerList, setSortedCustomerList] = useState([]);
function sortCustomer() {
const sortedCustomer = [...customerList];
let sortCount = counter;
//check the current sortCount, if it is 3 then go back to 1, if not then increase by 1
if (sortCount === 3) {
sortCount = 1;
setCounter(1);
} else {
sortCount += 1;
setCounter(sortCount);
}
console.log(sortCount);
if (sortCount < 3) {
sortedCustomer.sort(function (x, y) {
if (sortCount === 1) {
return x.contacted === y.contacted
? 0
: x.contacted === "Yes"
? -1
: 1;
} else if (sortCount === 2) {
return x.contacted === y.contacted
? 0
: x.contacted === "Yes"
? 1
: -1;
}
});
setCustomerList(sortedCustomer);
} else {
setCustomerList(customerList);
}
}
<th onClick={() => sortCustomer()}>Contacted?</th>
I have implemented a sort function onClick method. So first click returns the descending order list, 2nd click returns the ascending order list but the 3rd click does not return the original customerList. how to solve this problem.
const [customerList, setCustomerList] = useState([]); //store all that information of the database in a list
const [counter, setCounter] = useState(0);
const [sortedCustomerList, setSortedCustomerList] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/customers").then((response) => {
setCustomerList(response.data);
setSortedCustomerList(response.data);
});
}, []);
function sortCustomer() {
const sortedCustomer = [...customerList];
let sortCount = counter;
if (sortCount === 3) {
sortCount = 1;
setCounter(1);
} else {
sortCount += 1;
setCounter(sortCount);
}
if (sortCount < 3) {
sortedCustomer.sort(function (x, y) {
if (sortCount === 1) {
return x.contacted === y.contacted
? 0
: x.contacted === "Yes"
? -1
: 1;
} else if (sortCount === 2) {
return x.contacted === y.contacted
? 0
: x.contacted === "Yes"
? 1
: -1;
}
});
setSortedCustomerList(sortedCustomer);
} else {
setSortedCustomerList(customerList);
}
}
Use sortedCustomerList to print on the screen
<th onClick={() => sortCustomer()}>Contacted?</th>