I have a table and first column position is sticky. The first row is the date of the month. and the first column is student information. I want a button onClick action is scroll the specific column(today column) to the position next to the first column. How can I do that?
Any suggestion is appreciated.
After I assign ref to the today's column, The scrollIntoView() function work. The column I want to show will under the sticky column. So I want to use scrollTo() function to provide accurate position. The issue is that scrollTo() doesn't work. Which element should I assign useRef. table, tableRow or tableData(tableHead)?
type ListProps = {
AppStore: AppStore;
TaskScheduleStore: TaskScheduleStore;
EmployeeShiftStore: EmployeeShiftStore;
BusRouteStore: BusRouteStore;
};
const List: React.FC<ListProps> = ({
AppStore,
TaskScheduleStore,
EmployeeShiftStore,
BusRouteStore,
}) => {
const myRef = useRef<HTMLTableElement>(null);
return (
<Container>
<Card>
<div className='flex flex-row justify-between items-center'>
<div className='flex flex-row items-center'>
{format(currentDate, 'yyyy年M月')}
<button
onClick={() => {
// here is the button I want to scroll the column to specific position.
var table = document.getElementById(
'employeeShiftTable'
) as HTMLTableElement;
console.log(table?.scrollWidth)
// here can print the table width.
if (myRef.current !== null) {
myRef.current.scrollBy({
top: 100,
left: 400,
behavior: 'smooth',
});
}
}}
>
<span>Today</span>
</button>
</div>
</div>
<div>
<table id='employeeShiftTable' ref={myRef}>
<thead>
<tr>
<th>
corner
</th>
{Array(getDaysInMonth(currentDate))
.fill(currentDate)
.map((date, index) => {
return (
<th>
<div>
{index + 1}
</div>
</th>
);
})}
</tr>
</thead>
<tbody>
{employeeShifts.map((data, rowIndex) => (
<tr>
<th className='headcol'>
<div>
{rowIndex}
</div>
<div className='flex flex-row align-items-center jobTitle'>
00012 Name
</div>
</th>
{Array(getDaysInMonth(currentDate))
.fill(1)
.map((daea, columnIndex) => (
<td>
<div style={{ padding: '12px' }}>
test
</div>
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</Card>
</Container>
);
};
export default inject(
({ AppStore, TaskScheduleStore, EmployeeShiftStore, BusRouteStore }) => ({
AppStore,
TaskScheduleStore,
EmployeeShiftStore,
BusRouteStore,
})
)(observer(List));
an easy solution is to use an a tag with href="#id" and on the element that you want to scroll give it the same id that you mentioned in the href
Related
I'm working on a dynamic form where you can add and remove fields at will using React-hook-form. For this I made a table component where the input will be held. This is how it looks.
import {useFieldArray, useFormContext, Controller} from "react-hook-form";
import {cloneElement} from "react";
import {IoMdAdd, IoMdRemoveCircle} from "react-icons/io";
interface TableData {
tableName:string,
inputFields: {
title: string,
name: string,
inputComponent: React.ReactElement, // Did this since input can be text, select, or entire component
}[],
inputBlueprint: object,
min?: number
};
const InputTable = ({tableName, inputFields, inputBlueprint, min}: TableData) => {
const {fields, remove, append} = useFieldArray({name: tableName});
const {register, formState: {errors}, control} = useFormContext();
return (
<table className="table-auto border-collapse block m-auto w-fit max-w-xs max-h-48 overflow-auto sm:max-w-none my-3">
<thead className="text-center">
<tr>
{inputFields.map((input) => (
<td className="border-2 border-gray-400 px-5" key={input.title}>{input.title}</td>
))}
</tr>
</thead>
<tbody>
{fields.map((field, index) => (
<tr key={field.id}>
{inputFields.map((input) => (
<td key={input.title} className="border-gray-400 border-2 p-0">
{input.inputComponent.type === "input" && cloneElement(input.inputComponent, {
className: "bg-transparent outline-none block w-full focus:bg-gray-400 dark:focus:bg-gray-500 p-1",
...register(`${tableName}.${index}.${input.name}` as const)
})
}
{input.inputComponent.type !== "input" && //This doesn't work at all
<Controller
name={`${tableName}.${index}.${input.name}`}
control={control}
defaultValue=""
render={({field: {onChange, value}}) => {return input.inputComponent}}
/>
}
{errors[tableName]?.[index]?.[input.name] &&
<p className="bg-red-400 p-1">
{errors[tableName][index][input.name]?.message}
</p>
}
</td>
))}
{(min === undefined || min <= index) &&
<td onClick={() => remove(index)}><IoMdRemoveCircle className="text-red-600 text-2xl"/></td>
}
</tr>
))}
<tr>
<td onClick={() => append(inputBlueprint)} className="bg-green-500 border-gray-400 border-2"
colSpan={inputFields.length}>
<IoMdAdd className="m-auto"/>
</td>
</tr>
{errors[tableName] &&
<tr>
<td className="max-w-fit text-center">
{errors[tableName].message}
</td>
</tr>}
</tbody>
</table>
)
}
export default InputTable
It works for the most part with regular inputs (html input and select) but I'm having problems since I'm using Material's UI Autocomplete component for suggestions in some fields and since React-hook-form uses unregistered components and MUI uses registered, they really clash. Is there a better way to do this? I have thought about using the children prop but I'm not entirely sure if this would better the situation.
The infinite scroll component is in a table and its inside a scrollable page. i tried giving
id="scrollableDiv" to every div in the page and also <html> in index.html still no use. when i remove the scrollableTarget="scrollableDiv" the fetchdata works untill the parent scroll bar in the bottom. after that fetchData function not working. when i forcefully scroll the parent scroll fetchdata works.
But i want it to work on scrolling the table. Not when scrolling the parent(i mean the whole page), anyone tell me where should i assingn id="scrollableDiv". There is no div with height specified
Here is the code,
const fetchMoreData = () => {
console.log("new more data");
const temp = [...ingestStats];
setTimeout(() => {
setIngestStats((prev) => prev.concat(temp));
}, 1500);};
<div className="row">
<div className="col-xl-12">
<div className="card dashboard-table">
{/* /.card-header */}
<div className="card-body p-0" id="collapse1">
<InfiniteScroll
dataLength={ingestStats.length}
next={fetchMoreData}
hasMore={ingestStats.length < 40 ? true : false}
loader={
<p style={{ textAlign: "center" }}>
<b>Loading...</b>
</p>
}
endMessage={
<p style={{ textAlign: "center" }}>
<b>Yay! You have seen it all</b>
</p>
}
scrollableTarget="scrollableDiv"
>
<table className="table table-hover table-borderless text-center table-sm table-responsive">
<thead>
<tr>
<th>Activity</th>
<th>
Time Stamp{" "}
<span href="#0">
<i className="fas fa-angle-down" />
</span>
</th>
<th>
Status{" "}
<span href="#0">
<i className="fas fa-angle-down" />
</span>
</th>
</tr>
</thead>
<tbody>
{ingestStats &&
ingestStats.map((item, index) => (
<tr key={`${item.time}-${index}`}>
<td>{item.activity}</td>
<td>{item.time}</td>
<td>
{item.isActive ? (
<span className="status-success">
Success
</span>
) : (
<span className="status-failed">
Success
</span>
)}
</td>
</tr>
))}
</tbody>
</table>
</InfiniteScroll>
</div>
{/* /.card-body */}
</div>
</div>
</div>
I was encountering a similar problem where the infinitescroll element would take the whole window in order to scroll... However, there is a small fix for this. You actually just need to add 'height' to the InfiniteScroll element and all your problems will be gone. That is the reason why it won't trigger your fetch data. See down for an example:
const [fetchIsLoading, setFetchIsLoading] = useState(false);
const [contacts, setContacts] = useState([]);
const loadMoreData = () => {
if (fetchIsLoading) {
return;
}
setFetchIsLoading(true);
fetch('https://randomuser.me/api/?results=10&inc=name,gender,email,nat,picture&noinfo')
.then((res) => res.json())
.then((body) => {
setContacts([...contacts, ...body.results]);
setFetchIsLoading(false);
})
.catch(() => {
setFetchIsLoading(false);
});
};
useEffect(() => {
loadMoreData();
}, []);
<div // < ------------- top level div with id 'scrollableDiv' is not even needed... but it works (if you use it or not)
// id="scrollableDiv"
// style={{height: 400}}
>
<InfiniteScroll style={{
// height: 400 <-------- this does not work!
}}
dataLength={contacts.length}
next={loadMoreData}
hasMore={true}//contacts.length < 15
loader={
<Skeleton
avatar
paragraph={{
rows: 1,
}}
active
/>
}
height={400} // <--------- however, this works through the way this infinite scroll is set up.
endMessage={<Divider plain>It is all, nothing more 🤐</Divider>}
//scrollableTarget="scrollableDiv" <-------- this is not even needed the way this infinite scroll is set up. Even though you point it to the top level div it works either way...
>
<List
dataSource={contacts}
renderItem={(item) => (
<List.Item key={item.email}>
<List.Item.Meta
avatar={<Avatar src={item.picture.large} />}
title={<div style={{ color: '#54BEC6', cursor: 'pointer' }}>{item.name.last}</div>}
description={item.email}
onClick={(event) => onClickContactsTab(event, item.name.last)}
/>
{
// Use this section to put extra information about the user
//<div>Extra info?</div>
}
</List.Item>
)}
/>
</InfiniteScroll>
</div>
I have two json inside a react app and I want to filter the ids.
{data.investments.map((item) => (
<div className="tipo">
<h1 key={item.id}>{item.description}</h1>
<h2>
Rendimento total: <span>valor</span>
</h2>
{/* SECOND LOOP */}
{data.reports.map((rep) => (
<div>
<Tabela reports={data.reports} tipoInvestimento={item.id} />
</div>
))}
</div>
))}
On the element inside the second loop, I need to show only the elements that match the Id with the first loop
import React from "react";
export const Tabela = ({ reports }, { tipoInvestimento }) => {
return (
<table>
{reports.map((r) => {
if (r.id === tipoInvestimento)
return (
<tr key={r.id}>
<div>
<td>
{r.month}/{r.year}
</td>
<td>R$ {r.value.toFixed(2)}</td>
</div>
<td>{tipoInvestimento}</td>
</tr>
)}}
</table>
);
};
but nothing shows. Is there any other form to do it?
Thanks in advance.
You can use Array.filter() function to only iterate over some of the items based on a condition:
{data.investments.map((item) => (
<div className="tipo">
<h1 key={item.id}>{item.description}</h1>
<h2>
Rendimento total: <span>valor</span>
</h2>
{/* SECOND LOOP */}
{data.reports.filter(r => r.id === item.id).map((rep) => (
<div>
<Tabela reports={data.reports} tipoInvestimento={item.id} />
</div>
))}
</div>
))}
TableItem component added without any data in UI. Could somebody help on this. On refereshing the UI, added data is shown with details in TableItem component.
Table Component Code
import TableItem from "./TableItem";
function Table({ searchWord }) {
const dispatch = useDispatch();
const dictData = useSelector((state) => state.dictionary);
useEffect(() => {
dispatch(getDictionaryAsync());
}, [dispatch]);
return (
<table className="table table-striped">
<thead>
<tr>
<th scope="col">Word</th>
<th scope="col">Description</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{dictData &&
dictData
.filter((e) =>
searchWord === ""
? e
: e.word &&
e.word.toLowerCase().includes(searchWord.toLowerCase())
)
.map((item) => (
<TableItem item={item} key={item.id} searchWord={searchWord} />
))}
</tbody>
</table>
);
}
export default Table;
Below is the TableItem Component Code which i am trying to update,
When i add a word to dictionary it will fetch the details from the server and display it in the React app.
function TableItem({ item }) {
const [modal, setModal] = useState(false);
const openModal = () => {
setModal(true);
};
return (
<>
<tr key={item.id}>
<td style={{ textTransform: "capitalize" }}>{item.word}</td>
<td>
<b style={{ textTransform: "capitalize" }}>
{item.items && item.items[0].category} -{" "}
</b>
{item.items && truncate(item.items[0].definitions[0])}
</td>
<td>
<button className="btn btn-danger btn-sm " onClick={openModal}>
View
</button>
</td>
</tr>
<Modal isOpen={modal} ariaHideApp={true}>
<div className="modal-header">
<h3 className="modal-word-header">
{item.word && item.word.toUpperCase()}
</h3>
<button
className="btn btn-danger btn-sm"
onClick={() => setModal(false)}
>
<i class="fa fa-times" aria-hidden="true"></i>
</button>
</div>
<div className="model-content">
<p>
{item.items &&
item.items.map((e) => {
return (
<>
<i>{e.category}</i>
<ul>
{e.definitions.map((def) => {
return <li>{def}</li>;
})}
</ul>
</>
);
})}
</p>
</div>
</Modal>
</>
);
}
Better add your TableItem component code!
Below code works fine and updated the UI on change in the Data in TableItem,
useEffect(() => {
dispatch(getDictionaryAsync());
}, [dispatch, dictData]); *<--updated code*
I'm new in React and I have to create a CRUD table for my DB.
I created a simple table as you can see:
class ListPromoCatalog extends React.Component {
constructor(props) {
super(props);
this.state = {
promos: [],
message: null
};
this.refreshPromo = this.refreshPromo.bind(this)
}
componentDidMount() { //React defines a component lifecycle
this.refreshPromo();
}
refreshPromo() {
PromoCatalogService.retrieveAllPromo(PRIZES)//this would make the call to the REST API.
.then(
response => {
console.log(response);
this.setState({promos: response.data.viaggio})
}
)
}
PromoOffClicked(id) {
PromoCatalogService.promoOff(PRIZES, id)
.then(
response => {
console.log(response);
this.setState({message: `Promo ${id} OFF Successful`});
this.refreshPromo();
}
)
}
updatePromoClicked(id) {
console.log('update ' + id);
this.props.history.push(`/promos/${id}`);
}
addCourseClicked() {
this.props.history.push(`/promos/-1`);
}
render() {
return (
<div className="container">
<h3>All Promo</h3>
<div className="row">
<FileUploader/>
</div>
{this.state.message && <div className="alert alert-success">{this.state.message}</div>}
<div className="container">
<table className="table">
<thead>
<tr>
<th>Id</th>
<th>Item</th>
<th>Title</th>
<th>Description</th>
<th>Delete</th>
<th>Update</th>
</tr>
</thead>
<tbody>
{
this.state.promos.map(
promo =>
<tr key={promo.promoId}>
<td>{promo.promoId}</td>
<td>{promo.ijCod}</td>
<td>{promo.title}</td>
<td>{promo.description}</td>
<td>
<button className="btn btn-warning"
onClick={() => this.PromoOffClicked(promo.promoId)}>OFF
</button>
</td>
<td>
<button className="btn btn-success"
onClick={() => this.updatePromoClicked(promo.promoId)}>Update
</button>
</td>
</tr>
)
}
</tbody>
</table>
<div className="row">
<button className="btn btn-success" onClick={this.addCourseClicked}>Add</button>
</div>
</div>
</div>
)
}
}
export default ListPromoCatalog
I would like to do the same thing with a Material Table but I don't know how to put data and methods in my new class.
I would like to do the same thing with a Material Table but I don't know how to put data and methods in my new class.
render() {
return (
<>
<PanelHeader size="sm"/>
<div className="content">
<Row>
<Col xs={12}>
<Card>
<CardHeader>
<CardTitle tag="h4">Simple Table</CardTitle>
</CardHeader>
<CardBody>
<MDBDataTable
striped
bordered
small
data={data} //DATA SHOULD CONTAIN HEADERS AND COLUMNS
/>
</CardBody>
</Card>
</Col>
</Row>
</div>
</>
);
}