2

I have a project with nextjs and typescript.I use prime react as a UI kit for my project. On one of my pages I have a table and in this table I have a checkbox per row for select that row also if user dblClicked on a row it should navigate into another page.my issue is when I dblClick on a row checkbox is triggered(onSelectionChange method trigger). I know that prime table can get selectionMode='checkbox' prop and in that case checkbox triggered only if user clicks on a checkbox itself but I want if user singleClicks on a row onSelectionChange trigger too.

I wrote a wrapper for prime table component (<"Table someProps />)

this is my code

import React, {useEffect, useState} from 'react';
import {DataTableDataSelectableParams} from 'primereact/datatable';
import Table from '../Table';
import {AutoCompleteCompleteMethodParams} from 'primereact/autocomplete';
import {FlightStaticService} from '../../../adapter/FlightStaticService';
import OptionsMenu from '../../OptionsMenu/OptionsMenu';
import {InputSwitch} from 'primereact/inputswitch';
import InputWrapper from '../../InputWrapper/InputWrapper';
import {FlightService} from '../../../adapter/FlightService';
import ConfirmationStatus from '../../ConfirmationStatus/ConfirmationStatus';
import {useRouter} from 'next/router';

const flightStaticInstance = new FlightStaticService();
const flightInstance = new FlightService();

const FlightsListTable = () => {
    const [selectedRows, setSelectedRows] = useState<{ [key: string]: string | number | boolean }[]>([]);
    const [filteredAirlines, setFilteredAirlines] = useState([]);
    const [filteredAirports, setFilteredAirports] = useState([]);
    const [shouldUpdateTable, setShouldUpdateTable] = useState(false);
    const router = useRouter();

    const searchAirlines = (e: AutoCompleteCompleteMethodParams) => {
        if (!e.query) {
            e.query = 'as';
        }

        flightStaticInstance
            .getAirlines(e.query)
            .then((res) => {
                setFilteredAirlines(res.data.result);
            })
            .catch(e => {
                setFilteredAirlines([]);
            });
    };
    const searchAirports = (e: AutoCompleteCompleteMethodParams) => {
        if (!e.query) {
            e.query = 'meh';
        }

        flightStaticInstance
            .getAirports(e.query)
            .then((res) => {
                setFilteredAirports(res.data.result);
            })
            .catch(e => {
                setFilteredAirports([]);
            });
    };
    const isRowSelectable = (event: DataTableDataSelectableParams) => {
        const data = event.data;
        if (selectedRows.find((sel) => sel.id === data.id)) {
            return true;
        }
        return selectedRows.length < 2 && data.isActive;
    };

    useEffect(() => {
        if (shouldUpdateTable) {
            setShouldUpdateTable(false);
        }
    }, [shouldUpdateTable]);

    useEffect(() => {
        if (selectedRows.length > 0) {
            sessionStorage.setItem('flights', JSON.stringify(selectedRows));
        }
    }, [selectedRows]);

    const confirmStatusBodyTemplate = (rowData: any) => {
        return <ConfirmationStatus status={rowData.status}/>
    };

    const statusBodyTemplate = (rowData: any) => {
        return rowData.isActive ? 'فعال' : 'غیرفعال';
    };

    const optionsBodyTemplate = (rowData: any) => {
        return <OptionsMenu options={[{
            type: 'link',
            url: `/flight/${rowData.id}`,
            label: 'جزییات پرواز',
            iconName: 'icon-note-text2'
        }, {
            type: 'link',
            url: `/flight/${rowData.id}/edit`,
            label: 'ویرایش پرواز',
            iconName: 'icon-edit-2'
        },
            {
                type: 'link',
                url: `/flight/${rowData.id}/pricing?flightGroupTitle=${rowData.flightGroupTitle}`,
                label: 'تقویم قیمتی',
                iconName: 'icon-calendar-2'
            },
            {
                type: 'element',
                element: <div className='w-full' onClick={e => e.stopPropagation()}>
                    <InputWrapper labelClassName='text-grey-4' className='w-full' labelBeforeInput={true}
                                  labelBesideInput label='وضعیت'>
                        <InputSwitch
                            onChange={e => {
                                flightInstance.toggleFlightStatus(rowData.id).then(res => {
                                    setShouldUpdateTable(true);
                                }).catch(e => {

                                });
                            }
                            }
                            checked={rowData.isActive}
                            className='mr-auto'/>
                    </InputWrapper>
                </div>
            }
        ]}/>
    }

    return (
        <Table
            url="/Flight/GetFlights"
            shouldUpdateTable={shouldUpdateTable}
            filters={[
                {
                    name: 'airlineId',
                    label: 'ایرلاین',
                    type: 'autocomplete',
                    value: '',
                    suggestions: filteredAirlines,
                    completeMethod: searchAirlines,
                    optionValue: 'iata',
                    optionType: 'string',
                    fieldName: 'nameFa'
                },
                {
                    name: 'flightGroupTitle',
                    label: 'عنوان پرواز',
                    type: 'text',
                    value: ''
                },
                {
                    name: 'originAirPortId',
                    label: 'فرودگاه مبدا',
                    type: 'autocomplete',
                    value: '',
                    optionValue: 'iata',
                    optionType: 'string',
                    suggestions: filteredAirports,
                    completeMethod: searchAirports,
                    fieldName: 'nameFa'

                },
                {
                    name: 'destinationAirPortId',
                    label: 'فرودگاه مقصد',
                    type: 'autocomplete',
                    value: '',
                    optionValue: 'iata',
                    optionType: 'string',
                    suggestions: filteredAirports,
                    completeMethod: searchAirports,
                    fieldName: 'nameFa'
                }
            ]}
            columns={[
                {
                    field: 'airlineNameFa',
                    header: 'ایرلاین',
                },
                {
                    field: 'flightGroupTitle',
                    header: 'عنوان پرواز',
                    sortable: true,
                },
                {field: 'originCityNameFa', header: 'مبدا'},
                {field: 'destinationCityNameFa', header: 'مقصد'},
                {field: 'baggageAllowance', header: 'بار مجاز', sortable: true},
                {
                    field: 'confirmStatus',
                    header: 'وضعیت تایید',
                    body: confirmStatusBodyTemplate,
                },
                {
                    field: 'isActive',
                    header: 'وضعیت',
                    body: statusBodyTemplate,
                },
                {
                    field: 'options',
                    body: optionsBodyTemplate
                },
            ]}
            tableProps={{
                selection: selectedRows,
                onSelectionChange: (e) =>  setSelectedRows(e.value),
                isDataSelectable: isRowSelectable,
                showSelectAll: false,
                rowClassName: (data) => data.isActive ? '' : 'text-disabled',
                onRowDoubleClick: (e) =>  router.push(`/flight/${e.data.id}`)
            }}
        />
    );
};

export default FlightsListTable;
Ali Ehyaie
  • 1,116
  • 3
  • 13
  • 27

1 Answers1

2

OK here is a working Code Sandbox showing exactly what you want to do:

https://codesandbox.io/s/primereact-datatable-single-and-double-click-selection-0in9em?file=/src/demo/DataTableSelectionDemo.js

The trick is to handle onRowClick yourself.

const onRowClick = (event) => {
    if (event.originalEvent.detail === 1) {
      timer.current = setTimeout(() => {
        const selected = [...selectedProducts8];
        selected.push(event.data);
        setSelectedProducts8(selected);
      }, 300);
    }
  };

  const onRowDoubleClick = (e) => {
    clearTimeout(timer.current);
    console.log("dblclick");
  };

If you agree with this don't forget to select this as the right answer.

Melloware
  • 10,435
  • 2
  • 32
  • 62
  • it triggers checkbox on dblClick unfortunately but tnx anyway – Ali Ehyaie Dec 28 '22 at 06:25
  • @AliEhyaie no it doesn't I just ran the above again and If I single click it regsiters but not if I double click. If it does on yours then I just bumped the setTimeout to 300ms. Maybe your machine is slower than mine? – Melloware Dec 28 '22 at 15:12
  • ye it seems to work fine now thank you :)) but I want to know, is this the best option that we have? you know it is possible another system is weaker than mine and 300 ms timeout triggers the checkbox in dblClick again – Ali Ehyaie Dec 29 '22 at 06:46
  • Yes if you want both a single click and double click on the same element this is your only choice. – Melloware Dec 29 '22 at 14:48