draggableRuleList.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import {useState} from 'react';
  2. import {createPortal} from 'react-dom';
  3. import {DndContext, DragOverlay} from '@dnd-kit/core';
  4. import {arrayMove, SortableContext, verticalListSortingStrategy} from '@dnd-kit/sortable';
  5. import {SamplingRule} from 'sentry/types/sampling';
  6. import {DraggableRuleListItem, DraggableRuleListItemProps} from './draggableRuleListItem';
  7. import {
  8. DraggableRuleListSortableItem,
  9. SortableItemProps,
  10. } from './draggableRuleListSortableItem';
  11. import {isUniformRule} from './utils';
  12. export type DraggableRuleListUpdateItemsProps = {
  13. activeIndex: string;
  14. overIndex: string;
  15. reorderedItems: Array<string>;
  16. };
  17. type Props = Pick<SortableItemProps, 'disabled' | 'wrapperStyle'> &
  18. Pick<DraggableRuleListItemProps, 'renderItem'> & {
  19. items: Array<
  20. Omit<SamplingRule, 'id'> & {
  21. id: string;
  22. }
  23. >;
  24. onUpdateItems: (props: DraggableRuleListUpdateItemsProps) => void;
  25. };
  26. type State = {
  27. activeId?: string;
  28. };
  29. export function DraggableRuleList({
  30. items,
  31. onUpdateItems,
  32. renderItem,
  33. disabled = false,
  34. wrapperStyle = () => ({}),
  35. }: Props) {
  36. const [state, setState] = useState<State>({});
  37. const itemIds = items.map(item => item.id);
  38. const getIndex = itemIds.indexOf.bind(itemIds);
  39. const activeIndex = state.activeId ? getIndex(state.activeId) : -1;
  40. return (
  41. <DndContext
  42. onDragStart={({active}) => {
  43. if (!active) {
  44. return;
  45. }
  46. setState({activeId: active.id});
  47. }}
  48. onDragEnd={({over}) => {
  49. setState({activeId: undefined});
  50. if (over) {
  51. const overIndex = getIndex(over.id);
  52. if (activeIndex !== overIndex) {
  53. onUpdateItems({
  54. activeIndex,
  55. overIndex,
  56. reorderedItems: arrayMove(itemIds, activeIndex, overIndex),
  57. });
  58. }
  59. }
  60. }}
  61. onDragCancel={() => setState({activeId: undefined})}
  62. >
  63. <SortableContext items={itemIds} strategy={verticalListSortingStrategy}>
  64. {itemIds.map((itemId, index) => (
  65. <DraggableRuleListSortableItem
  66. key={itemId}
  67. id={itemId}
  68. index={index}
  69. renderItem={renderItem}
  70. disabled={
  71. disabled || isUniformRule({...items[index], id: Number(items[index].id)})
  72. }
  73. wrapperStyle={wrapperStyle}
  74. />
  75. ))}
  76. </SortableContext>
  77. {createPortal(
  78. <DragOverlay>
  79. {state.activeId ? (
  80. <DraggableRuleListItem
  81. value={itemIds[activeIndex]}
  82. renderItem={renderItem}
  83. wrapperStyle={wrapperStyle({
  84. index: activeIndex,
  85. isDragging: true,
  86. isSorting: false,
  87. })}
  88. />
  89. ) : null}
  90. </DragOverlay>,
  91. document.body
  92. )}
  93. </DndContext>
  94. );
  95. }