sortableWidget.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import {ComponentProps, useEffect} from 'react';
  2. import {useSortable} from '@dnd-kit/sortable';
  3. import styled from '@emotion/styled';
  4. import {Organization} from 'sentry/types';
  5. import theme from 'sentry/utils/theme';
  6. import withOrganization from 'sentry/utils/withOrganization';
  7. import WidgetCard from 'sentry/views/dashboardsV2/widgetCard';
  8. import {Widget} from './types';
  9. import DnDKitWidgetWrapper from './widgetWrapper';
  10. const TABLE_ITEM_LIMIT = 20;
  11. type Props = {
  12. dragId: string;
  13. index: string;
  14. isEditing: boolean;
  15. onDelete: () => void;
  16. onDuplicate: () => void;
  17. onEdit: () => void;
  18. organization: Organization;
  19. widget: Widget;
  20. widgetLimitReached: boolean;
  21. isMobile?: boolean;
  22. isPreview?: boolean;
  23. windowWidth?: number;
  24. };
  25. function SortableWidget(props: Props) {
  26. const {
  27. organization,
  28. widget,
  29. dragId,
  30. isEditing,
  31. widgetLimitReached,
  32. onDelete,
  33. onEdit,
  34. onDuplicate,
  35. isPreview,
  36. isMobile,
  37. windowWidth,
  38. index,
  39. } = props;
  40. const {
  41. attributes,
  42. listeners,
  43. setNodeRef,
  44. transform,
  45. isDragging: currentWidgetDragging,
  46. isSorting,
  47. } = useSortable({
  48. id: dragId,
  49. transition: null,
  50. });
  51. useEffect(() => {
  52. if (!currentWidgetDragging) {
  53. return undefined;
  54. }
  55. document.body.style.cursor = 'grabbing';
  56. return function cleanup() {
  57. document.body.style.cursor = '';
  58. };
  59. }, [currentWidgetDragging]);
  60. let widgetProps: ComponentProps<typeof WidgetCard> = {
  61. widget,
  62. isEditing,
  63. widgetLimitReached,
  64. onDelete,
  65. onEdit,
  66. onDuplicate,
  67. isSorting,
  68. hideToolbar: isSorting,
  69. currentWidgetDragging,
  70. showContextMenu: true,
  71. isPreview,
  72. showWidgetViewerButton: organization.features.includes('widget-viewer-modal'),
  73. index,
  74. };
  75. if (organization.features.includes('dashboard-grid-layout')) {
  76. widgetProps = {
  77. ...widgetProps,
  78. isMobile,
  79. windowWidth,
  80. // TODO(nar): These aren't necessary for supporting RGL
  81. isSorting: false,
  82. currentWidgetDragging: false,
  83. tableItemLimit: TABLE_ITEM_LIMIT,
  84. };
  85. return (
  86. <GridWidgetWrapper>
  87. <WidgetCard {...widgetProps} />
  88. </GridWidgetWrapper>
  89. );
  90. }
  91. const initialStyles: ComponentProps<typeof DnDKitWidgetWrapper>['animate'] = {
  92. zIndex: 'auto',
  93. };
  94. widgetProps = {...widgetProps, draggableProps: {attributes, listeners}};
  95. return (
  96. <DnDKitWidgetWrapper
  97. ref={setNodeRef}
  98. displayType={widget.displayType}
  99. layoutId={dragId}
  100. style={{
  101. // Origin is set to top right-hand corner where the drag handle is placed.
  102. // Otherwise, set the origin to be the top left-hand corner when swapping widgets.
  103. originX: currentWidgetDragging ? 1 : 0,
  104. originY: 0,
  105. boxShadow: currentWidgetDragging ? theme.dropShadowHeavy : 'none',
  106. borderRadius: currentWidgetDragging ? theme.borderRadius : undefined,
  107. }}
  108. animate={
  109. transform
  110. ? {
  111. x: transform.x,
  112. y: transform.y,
  113. scaleX: transform?.scaleX && transform.scaleX <= 1 ? transform.scaleX : 1,
  114. scaleY: transform?.scaleY && transform.scaleY <= 1 ? transform.scaleY : 1,
  115. zIndex: currentWidgetDragging ? theme.zIndex.modal : 'auto',
  116. }
  117. : initialStyles
  118. }
  119. transformTemplate={(___transform, generatedTransform) => {
  120. if (isEditing && !!transform) {
  121. return generatedTransform;
  122. }
  123. return 'none';
  124. }}
  125. transition={{
  126. duration: !currentWidgetDragging ? 0.25 : 0,
  127. easings: {
  128. type: 'spring',
  129. },
  130. }}
  131. >
  132. <WidgetCard {...widgetProps} />
  133. </DnDKitWidgetWrapper>
  134. );
  135. }
  136. export default withOrganization(SortableWidget);
  137. const GridWidgetWrapper = styled('div')`
  138. height: 100%;
  139. `;