scratchpad.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import {useCallback} from 'react';
  2. import styled from '@emotion/styled';
  3. import * as echarts from 'echarts/core';
  4. import {Button} from 'sentry/components/button';
  5. import Panel from 'sentry/components/panels/panel';
  6. import {IconAdd} from 'sentry/icons';
  7. import {space} from 'sentry/styles/space';
  8. import {trackAnalytics} from 'sentry/utils/analytics';
  9. import {MetricWidgetQueryParams} from 'sentry/utils/metrics';
  10. import useOrganization from 'sentry/utils/useOrganization';
  11. import usePageFilters from 'sentry/utils/usePageFilters';
  12. import {DDM_CHART_GROUP, MIN_WIDGET_WIDTH} from 'sentry/views/ddm/constants';
  13. import {useDDMContext} from 'sentry/views/ddm/context';
  14. import {MetricWidget} from './widget';
  15. export function MetricScratchpad() {
  16. const {setSelectedWidgetIndex, selectedWidgetIndex, widgets, updateWidget, addWidget} =
  17. useDDMContext();
  18. const {selection} = usePageFilters();
  19. const organization = useOrganization();
  20. const handleChange = useCallback(
  21. (index: number, widget: Partial<MetricWidgetQueryParams>) => {
  22. updateWidget(index, widget);
  23. },
  24. [updateWidget]
  25. );
  26. const Wrapper =
  27. widgets.length === 1 ? StyledSingleWidgetWrapper : StyledMetricDashboard;
  28. echarts.connect(DDM_CHART_GROUP);
  29. return (
  30. <Wrapper>
  31. {widgets.map((widget, index) => (
  32. <MetricWidget
  33. key={index}
  34. index={index}
  35. onSelect={setSelectedWidgetIndex}
  36. isSelected={selectedWidgetIndex === index}
  37. onChange={handleChange}
  38. widget={widget}
  39. datetime={selection.datetime}
  40. projects={selection.projects}
  41. environments={selection.environments}
  42. />
  43. ))}
  44. <AddWidgetPanel
  45. onClick={() => {
  46. trackAnalytics('ddm.widget.add', {
  47. organization,
  48. });
  49. addWidget();
  50. }}
  51. >
  52. <Button icon={<IconAdd isCircled />}>Add widget</Button>
  53. </AddWidgetPanel>
  54. </Wrapper>
  55. );
  56. }
  57. const StyledMetricDashboard = styled('div')`
  58. display: grid;
  59. grid-template-columns: repeat(3, minmax(${MIN_WIDGET_WIDTH}px, 1fr));
  60. gap: ${space(2)};
  61. @media (max-width: ${props => props.theme.breakpoints.xxlarge}) {
  62. grid-template-columns: repeat(2, minmax(${MIN_WIDGET_WIDTH}px, 1fr));
  63. }
  64. @media (max-width: ${props => props.theme.breakpoints.xlarge}) {
  65. grid-template-columns: repeat(1, minmax(${MIN_WIDGET_WIDTH}px, 1fr));
  66. }
  67. grid-auto-rows: 1fr;
  68. `;
  69. const StyledSingleWidgetWrapper = styled('div')`
  70. display: grid;
  71. grid-template-columns: minmax(${MIN_WIDGET_WIDTH}px, 90%) minmax(180px, 10%);
  72. @media (max-width: ${props => props.theme.breakpoints.xlarge}) {
  73. grid-template-columns: repeat(1, minmax(${MIN_WIDGET_WIDTH}px, 1fr));
  74. }
  75. gap: ${space(2)};
  76. grid-auto-rows: 1fr;
  77. `;
  78. const AddWidgetPanel = styled(Panel)`
  79. width: 100%;
  80. height: 100%;
  81. margin-bottom: 0;
  82. padding: ${space(4)};
  83. font-size: ${p => p.theme.fontSizeExtraLarge};
  84. display: flex;
  85. justify-content: center;
  86. align-items: center;
  87. border: 1px dashed ${p => p.theme.border};
  88. &:hover {
  89. background-color: ${p => p.theme.backgroundSecondary};
  90. cursor: pointer;
  91. }
  92. `;