import { Button, Callout, Classes } from '@blueprintjs/core'; import type { Jcoupling, Signal1D } from '@zakodium/nmr-types'; import dlv from 'dlv'; import type { CSSProperties } from 'react'; import { useCallback, useMemo, useRef } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; import { FaPlus, FaRegTrashAlt } from 'react-icons/fa'; import { Toolbar } from 'react-science/ui'; import type { CellProps } from 'react-table'; import { multiplets } from '../../../../../../data/constants/Multiplets.js'; import { isSpectrum1D } from '../../../../../../data/data1d/Spectrum1D/index.js'; import { NumberInput2Controller } from '../../../../../elements/NumberInput2Controller.js'; import type { Column } from '../../../../../elements/ReactTable/ReactTable.js'; import ReactTable from '../../../../../elements/ReactTable/ReactTable.js'; import { Select2Controller } from '../../../../../elements/Select2Controller.js'; import { useTabsController } from '../../../../../elements/TabsProvider.js'; import useSpectrum from '../../../../../hooks/useSpectrum.js'; import { hasCouplingConstant } from '../../../../../panels/extra/utilities/MultiplicityUtilities.js'; import { useEvent } from '../../../../../utility/Events.js'; import { useEventFocusInput } from '../SignalsContent.js'; const styles: Record<'input' | 'select' | 'column', CSSProperties> = { input: { width: '100%', padding: '0.25rem 0.5rem', }, select: { height: '26px', width: '100%', }, column: { padding: '2px', }, }; interface SignalJCouplingsTableProps { index: number; } function getJCouplingKey( signalIndex: number, jIndex: any, jCouplingKey: keyof Jcoupling, ) { return `signals[${signalIndex}].js.${jIndex}.${jCouplingKey}`; } function getCouplingMinErrorMessage(errors: any, index: any) { return dlv(errors, `signals.${index}.js.root.message`); } export function SignalJCouplingsTable(props: SignalJCouplingsTableProps) { const { focusSource, setFocusSource } = useEventFocusInput(); const { setValue, control, setFocus, formState: { errors }, } = useFormContext(); const signals = useWatch({ name: 'signals' }); const { selectedTabId: signalIndex = 0 } = useTabsController(); const signal: Signal1D | undefined = signals?.[signalIndex] || {}; const lastSelectedCouplingIndexRef = useRef(null); const spectrum = useSpectrum(); useEvent({ onClick: ({ xPPM, shiftKey }) => { if ( props.index === signalIndex && typeof lastSelectedCouplingIndexRef.current === 'number' && shiftKey && focusSource === 'coupling' ) { setValue( getJCouplingKey( signalIndex, lastSelectedCouplingIndexRef.current, 'coupling', ), xPPM, ); } }, onBrushEnd: (options) => { const { range: [from, to], shiftKey, } = options; if ( props.index === signalIndex && typeof lastSelectedCouplingIndexRef.current === 'number' && shiftKey && isSpectrum1D(spectrum) && focusSource === 'coupling' ) { const value = Math.abs(to - from) * spectrum.info.originFrequency; setValue( getJCouplingKey( signalIndex, lastSelectedCouplingIndexRef.current, 'coupling', ), value, ); } }, }); const addHandler = useCallback( (data: Jcoupling[]) => { const coupling = { multiplicity: 'm', coupling: '', }; setValue(`signals[${signalIndex}].js`, [...data, coupling]); lastSelectedCouplingIndexRef.current = data.length; }, [setValue, signalIndex], ); const deleteHandler = useCallback( (data: any, index: number) => { const jCouplings = data.filter( (_: any, columnIndex: any) => columnIndex !== index, ); setValue(`signals[${signalIndex}].js`, jCouplings); if (lastSelectedCouplingIndexRef.current === index) { lastSelectedCouplingIndexRef.current = null; } }, [setValue, signalIndex], ); function deleteAllHandler() { setValue(`signals[${signalIndex}].js`, []); lastSelectedCouplingIndexRef.current = null; } const COLUMNS = useMemo>>( () => [ { Header: '#', style: { width: '25px', ...styles.column }, accessor: (_: any, index) => index + 1, }, { Header: 'Multiplicity', style: { padding: 0, ...styles.column }, Cell: ({ row }: CellProps) => { return ( { const name = getJCouplingKey( signalIndex, row.index, 'coupling', ); if (!hasCouplingConstant(value)) { setValue(name, ''); } else { requestAnimationFrame(() => { setTimeout(() => { setFocus(name, { shouldSelect: true }); }, 0); }); } }} fill selectedButtonProps={{ size: 'small', variant: 'minimal' }} /> ); }, }, { Header: 'J (Hz)', style: { padding: 0, ...styles.column }, Cell: ({ row }: CellProps) => { return ( ); }, }, { Header: '', style: { width: '70px', ...styles.column }, id: 'action-button', Cell: ({ data, row }: CellProps) => { const record: any = row.original; return (
{!record?.name && ( )}
); }, }, ], [control, deleteHandler, setFocus, setValue, signalIndex], ); function selectRowHandler(index: any) { setFocusSource('coupling'); lastSelectedCouplingIndexRef.current = index; } const minErrorMessage = getCouplingMinErrorMessage(errors, props.index); return (
{minErrorMessage && ( {minErrorMessage} )}
} tooltip="Add a new J coupling" intent="success" onClick={() => addHandler(signal?.js || [])} /> } tooltip="Delete all J couplings" intent="danger" onClick={deleteAllHandler} />
selectRowHandler(rowData.index)} emptyDataRowText="No J Couplings" />
); }