/** * WordPress dependencies */ import { Button, ColorIndicator, Icon } from '@safe-wordpress/components'; import { useDispatch } from '@safe-wordpress/data'; import { useRef } from '@safe-wordpress/element'; import * as Icons from '@safe-wordpress/icons'; import { _x } from '@safe-wordpress/i18n'; type IconKey = keyof typeof Icons; /** * External dependencies */ import { clsx } from 'clsx'; import { useDrag, useDrop, type XYCoord } from 'react-dnd'; import { DeleteButton } from '@nelio-content/components'; import type { PostStatusSlug } from '@nelio-content/types'; /** * Internal dependencies */ import './style.scss'; import { store as NC_CUSTOM_STATUSES } from '~/nelio-content-pages/settings/custom-statuses/store'; import { useStatus, useIsSaving, } from '~/nelio-content-pages/settings/custom-statuses/hooks'; type DragItem = { index: number; id: PostStatusSlug; type: string; }; export type StatusProps = { readonly index: number; readonly slug: PostStatusSlug; readonly onMove: ( dragIndex: number, hoverIndex: number ) => void; readonly onDragStart: () => void; readonly onDragEnd: () => void; }; export const Status = ( { index, slug, onMove, onDragStart, onDragEnd, }: StatusProps ): JSX.Element | null => { const status = useStatus( slug ); const isSaving = useIsSaving(); const { openStatusEditor, removeStatus } = useDispatch( NC_CUSTOM_STATUSES ); const ref = useRef< HTMLDivElement >( null ); const [ _, dropReference ] = useDrop< DragItem >( { accept: 'status', hover: ( dragItem, monitor ) => { if ( ! ref.current ) { return; } const dragIndex = dragItem.index; const hoverIndex = index; // Don't replace items with themselves if ( dragIndex === hoverIndex ) { return; } // Determine rectangle on screen const hoverBoundingRect = ref.current?.getBoundingClientRect(); // Get vertical middle const hoverMiddleY = ( hoverBoundingRect.bottom - hoverBoundingRect.top ) / 2; // Determine mouse position const clientOffset = monitor.getClientOffset(); // Get pixels to the top const hoverClientY = ( clientOffset as XYCoord ).y - hoverBoundingRect.top; // Only perform the move when the mouse has crossed half of the items height // When dragging downwards, only move when the cursor is below 50% // When dragging upwards, only move when the cursor is above 50% // Dragging downwards if ( dragIndex < hoverIndex && hoverClientY < hoverMiddleY ) { return; } // Dragging upwards if ( dragIndex > hoverIndex && hoverClientY > hoverMiddleY ) { return; } // Time to actually perform the action onMove( dragIndex, hoverIndex ); // Note: we're mutating the monitor item here! // Generally it's better to avoid mutations, // but it's good here for the sake of performance // to avoid expensive index searches. dragItem.index = hoverIndex; }, } ); const [ { isDragging }, dragReference ] = useDrag( { type: 'status', item: () => { onDragStart(); return { id: status?.slug, index }; }, end: onDragEnd, collect: ( monitor ) => ( { isDragging: monitor.isDragging(), } ), } ); dropReference( dragReference( ref ) ); if ( ! status ) { return null; } return (