@perses-dev/dashboards 0.0.0-snapshot-ts-panel-actions-76080ec → 0.0.0-snapshot-reverse-proxy-75afbd7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/dist/cjs/components/DashboardStickyToolbar/DashboardStickyToolbar.js +0 -2
  2. package/dist/cjs/components/DeletePanelGroupDialog/DeletePanelGroupDialog.js +5 -20
  3. package/dist/cjs/components/GridLayout/GridItemContent.js +79 -17
  4. package/dist/cjs/components/GridLayout/GridLayout.js +78 -126
  5. package/dist/cjs/components/GridLayout/Row.js +150 -0
  6. package/dist/cjs/components/GridLayout/index.js +1 -0
  7. package/dist/cjs/components/LeaveDialog/LeaveDialog.js +81 -0
  8. package/dist/cjs/components/LeaveDialog/index.js +30 -0
  9. package/dist/cjs/components/Panel/Panel.js +7 -2
  10. package/dist/cjs/components/Panel/PanelActions.js +37 -14
  11. package/dist/cjs/components/Panel/PanelHeader.js +35 -15
  12. package/dist/cjs/components/Panel/index.js +1 -0
  13. package/dist/cjs/components/PanelDrawer/PanelDrawer.js +58 -21
  14. package/dist/cjs/components/PanelDrawer/PanelEditorForm.js +202 -180
  15. package/dist/cjs/components/PanelDrawer/PanelPreview.js +3 -0
  16. package/dist/cjs/components/PanelGroupDialog/PanelGroupDialog.js +9 -21
  17. package/dist/cjs/components/PanelGroupDialog/PanelGroupEditorForm.js +35 -15
  18. package/dist/cjs/components/QueryViewerDialog/QueryViewerDialog.js +121 -0
  19. package/dist/cjs/components/QueryViewerDialog/index.js +30 -0
  20. package/dist/cjs/components/SaveChangesConfirmationDialog/SaveChangesConfirmationDialog.js +2 -1
  21. package/dist/cjs/components/Variables/ListVariableListBox.js +201 -0
  22. package/dist/cjs/components/Variables/Variable.js +130 -72
  23. package/dist/cjs/components/Variables/VariableEditor.js +22 -15
  24. package/dist/cjs/components/Variables/index.js +1 -0
  25. package/dist/cjs/components/index.js +3 -1
  26. package/dist/cjs/constants/user-interface-text.js +4 -2
  27. package/dist/cjs/context/DashboardProvider/DashboardProvider.js +7 -8
  28. package/dist/cjs/context/DashboardProvider/DashboardProviderWithQueryParams.js +4 -4
  29. package/dist/cjs/context/DashboardProvider/duplicate-panel-slice.js +1 -1
  30. package/dist/cjs/context/DashboardProvider/panel-editor-slice.js +2 -2
  31. package/dist/cjs/context/DashboardProvider/panel-group-editor-slice.js +6 -2
  32. package/dist/cjs/context/DashboardProvider/panel-group-slice.js +1 -0
  33. package/dist/cjs/context/DashboardProvider/view-panel-slice.js +10 -3
  34. package/dist/cjs/context/PanelEditorProvider/PanelEditorProvider.js +49 -0
  35. package/dist/cjs/context/PanelEditorProvider/index.js +23 -0
  36. package/dist/cjs/context/VariableProvider/VariableProvider.js +1 -1
  37. package/dist/cjs/context/index.js +1 -0
  38. package/dist/cjs/context/useDashboard.js +5 -4
  39. package/dist/cjs/views/ViewDashboard/DashboardApp.js +7 -3
  40. package/dist/cjs/views/ViewDashboard/ViewDashboard.js +9 -8
  41. package/dist/components/DashboardStickyToolbar/DashboardStickyToolbar.d.ts.map +1 -1
  42. package/dist/components/DashboardStickyToolbar/DashboardStickyToolbar.js +0 -2
  43. package/dist/components/DashboardStickyToolbar/DashboardStickyToolbar.js.map +1 -1
  44. package/dist/components/DashboardToolbar/DashboardToolbar.d.ts +2 -2
  45. package/dist/components/DashboardToolbar/DashboardToolbar.d.ts.map +1 -1
  46. package/dist/components/DashboardToolbar/DashboardToolbar.js.map +1 -1
  47. package/dist/components/DeletePanelGroupDialog/DeletePanelGroupDialog.d.ts.map +1 -1
  48. package/dist/components/DeletePanelGroupDialog/DeletePanelGroupDialog.js +5 -15
  49. package/dist/components/DeletePanelGroupDialog/DeletePanelGroupDialog.js.map +1 -1
  50. package/dist/components/GridLayout/GridItemContent.d.ts.map +1 -1
  51. package/dist/components/GridLayout/GridItemContent.js +39 -18
  52. package/dist/components/GridLayout/GridItemContent.js.map +1 -1
  53. package/dist/components/GridLayout/GridLayout.d.ts +8 -0
  54. package/dist/components/GridLayout/GridLayout.d.ts.map +1 -1
  55. package/dist/components/GridLayout/GridLayout.js +72 -126
  56. package/dist/components/GridLayout/GridLayout.js.map +1 -1
  57. package/dist/components/GridLayout/Row.d.ts +17 -0
  58. package/dist/components/GridLayout/Row.d.ts.map +1 -0
  59. package/dist/components/GridLayout/Row.js +142 -0
  60. package/dist/components/GridLayout/Row.js.map +1 -0
  61. package/dist/components/GridLayout/index.d.ts +1 -0
  62. package/dist/components/GridLayout/index.d.ts.map +1 -1
  63. package/dist/components/GridLayout/index.js +1 -0
  64. package/dist/components/GridLayout/index.js.map +1 -1
  65. package/dist/components/LeaveDialog/LeaveDialog.d.ts +13 -0
  66. package/dist/components/LeaveDialog/LeaveDialog.d.ts.map +1 -0
  67. package/dist/components/LeaveDialog/LeaveDialog.js +70 -0
  68. package/dist/components/LeaveDialog/LeaveDialog.js.map +1 -0
  69. package/dist/components/LeaveDialog/index.d.ts +2 -0
  70. package/dist/components/LeaveDialog/index.d.ts.map +1 -0
  71. package/dist/components/LeaveDialog/index.js +15 -0
  72. package/dist/components/LeaveDialog/index.js.map +1 -0
  73. package/dist/components/Panel/HeaderIconButton.d.ts +1 -1
  74. package/dist/components/Panel/Panel.d.ts +6 -0
  75. package/dist/components/Panel/Panel.d.ts.map +1 -1
  76. package/dist/components/Panel/Panel.js +7 -2
  77. package/dist/components/Panel/Panel.js.map +1 -1
  78. package/dist/components/Panel/PanelActions.d.ts +5 -0
  79. package/dist/components/Panel/PanelActions.d.ts.map +1 -1
  80. package/dist/components/Panel/PanelActions.js +37 -14
  81. package/dist/components/Panel/PanelActions.js.map +1 -1
  82. package/dist/components/Panel/PanelHeader.d.ts +7 -1
  83. package/dist/components/Panel/PanelHeader.d.ts.map +1 -1
  84. package/dist/components/Panel/PanelHeader.js +36 -16
  85. package/dist/components/Panel/PanelHeader.js.map +1 -1
  86. package/dist/components/Panel/index.d.ts +1 -0
  87. package/dist/components/Panel/index.d.ts.map +1 -1
  88. package/dist/components/Panel/index.js +1 -0
  89. package/dist/components/Panel/index.js.map +1 -1
  90. package/dist/components/PanelDrawer/PanelDrawer.d.ts.map +1 -1
  91. package/dist/components/PanelDrawer/PanelDrawer.js +59 -22
  92. package/dist/components/PanelDrawer/PanelDrawer.js.map +1 -1
  93. package/dist/components/PanelDrawer/PanelEditorForm.d.ts.map +1 -1
  94. package/dist/components/PanelDrawer/PanelEditorForm.js +205 -183
  95. package/dist/components/PanelDrawer/PanelEditorForm.js.map +1 -1
  96. package/dist/components/PanelDrawer/PanelPreview.d.ts.map +1 -1
  97. package/dist/components/PanelDrawer/PanelPreview.js +4 -1
  98. package/dist/components/PanelDrawer/PanelPreview.js.map +1 -1
  99. package/dist/components/PanelGroupDialog/PanelGroupDialog.d.ts.map +1 -1
  100. package/dist/components/PanelGroupDialog/PanelGroupDialog.js +8 -15
  101. package/dist/components/PanelGroupDialog/PanelGroupDialog.js.map +1 -1
  102. package/dist/components/PanelGroupDialog/PanelGroupEditorForm.d.ts +1 -0
  103. package/dist/components/PanelGroupDialog/PanelGroupEditorForm.d.ts.map +1 -1
  104. package/dist/components/PanelGroupDialog/PanelGroupEditorForm.js +36 -16
  105. package/dist/components/PanelGroupDialog/PanelGroupEditorForm.js.map +1 -1
  106. package/dist/components/QueryViewerDialog/QueryViewerDialog.d.ts +9 -0
  107. package/dist/components/QueryViewerDialog/QueryViewerDialog.d.ts.map +1 -0
  108. package/dist/components/QueryViewerDialog/QueryViewerDialog.js +72 -0
  109. package/dist/components/QueryViewerDialog/QueryViewerDialog.js.map +1 -0
  110. package/dist/components/QueryViewerDialog/index.d.ts +2 -0
  111. package/dist/components/QueryViewerDialog/index.d.ts.map +1 -0
  112. package/dist/components/QueryViewerDialog/index.js +15 -0
  113. package/dist/components/QueryViewerDialog/index.js.map +1 -0
  114. package/dist/components/SaveChangesConfirmationDialog/SaveChangesConfirmationDialog.d.ts.map +1 -1
  115. package/dist/components/SaveChangesConfirmationDialog/SaveChangesConfirmationDialog.js +2 -1
  116. package/dist/components/SaveChangesConfirmationDialog/SaveChangesConfirmationDialog.js.map +1 -1
  117. package/dist/components/Variables/ListVariableListBox.d.ts +16 -0
  118. package/dist/components/Variables/ListVariableListBox.d.ts.map +1 -0
  119. package/dist/components/Variables/ListVariableListBox.js +141 -0
  120. package/dist/components/Variables/ListVariableListBox.js.map +1 -0
  121. package/dist/components/Variables/Variable.d.ts.map +1 -1
  122. package/dist/components/Variables/Variable.js +134 -76
  123. package/dist/components/Variables/Variable.js.map +1 -1
  124. package/dist/components/Variables/VariableEditor.d.ts.map +1 -1
  125. package/dist/components/Variables/VariableEditor.js +24 -17
  126. package/dist/components/Variables/VariableEditor.js.map +1 -1
  127. package/dist/components/Variables/index.d.ts +1 -0
  128. package/dist/components/Variables/index.d.ts.map +1 -1
  129. package/dist/components/Variables/index.js +1 -0
  130. package/dist/components/Variables/index.js.map +1 -1
  131. package/dist/components/index.d.ts +3 -1
  132. package/dist/components/index.d.ts.map +1 -1
  133. package/dist/components/index.js +3 -1
  134. package/dist/components/index.js.map +1 -1
  135. package/dist/constants/user-interface-text.d.ts +2 -0
  136. package/dist/constants/user-interface-text.d.ts.map +1 -1
  137. package/dist/constants/user-interface-text.js +4 -2
  138. package/dist/constants/user-interface-text.js.map +1 -1
  139. package/dist/context/DashboardProvider/DashboardProvider.d.ts +5 -5
  140. package/dist/context/DashboardProvider/DashboardProvider.d.ts.map +1 -1
  141. package/dist/context/DashboardProvider/DashboardProvider.js +7 -8
  142. package/dist/context/DashboardProvider/DashboardProvider.js.map +1 -1
  143. package/dist/context/DashboardProvider/DashboardProviderWithQueryParams.d.ts.map +1 -1
  144. package/dist/context/DashboardProvider/DashboardProviderWithQueryParams.js +5 -5
  145. package/dist/context/DashboardProvider/DashboardProviderWithQueryParams.js.map +1 -1
  146. package/dist/context/DashboardProvider/common.d.ts +1 -1
  147. package/dist/context/DashboardProvider/common.d.ts.map +1 -1
  148. package/dist/context/DashboardProvider/common.js.map +1 -1
  149. package/dist/context/DashboardProvider/duplicate-panel-slice.js +2 -2
  150. package/dist/context/DashboardProvider/duplicate-panel-slice.js.map +1 -1
  151. package/dist/context/DashboardProvider/panel-editor-slice.d.ts +1 -0
  152. package/dist/context/DashboardProvider/panel-editor-slice.d.ts.map +1 -1
  153. package/dist/context/DashboardProvider/panel-editor-slice.js +3 -3
  154. package/dist/context/DashboardProvider/panel-editor-slice.js.map +1 -1
  155. package/dist/context/DashboardProvider/panel-group-editor-slice.d.ts +1 -0
  156. package/dist/context/DashboardProvider/panel-group-editor-slice.d.ts.map +1 -1
  157. package/dist/context/DashboardProvider/panel-group-editor-slice.js +6 -2
  158. package/dist/context/DashboardProvider/panel-group-editor-slice.js.map +1 -1
  159. package/dist/context/DashboardProvider/panel-group-slice.d.ts +3 -0
  160. package/dist/context/DashboardProvider/panel-group-slice.d.ts.map +1 -1
  161. package/dist/context/DashboardProvider/panel-group-slice.js +1 -0
  162. package/dist/context/DashboardProvider/panel-group-slice.js.map +1 -1
  163. package/dist/context/DashboardProvider/view-panel-slice.d.ts +6 -2
  164. package/dist/context/DashboardProvider/view-panel-slice.d.ts.map +1 -1
  165. package/dist/context/DashboardProvider/view-panel-slice.js +10 -3
  166. package/dist/context/DashboardProvider/view-panel-slice.js.map +1 -1
  167. package/dist/context/PanelEditorProvider/PanelEditorProvider.d.ts +13 -0
  168. package/dist/context/PanelEditorProvider/PanelEditorProvider.d.ts.map +1 -0
  169. package/dist/context/PanelEditorProvider/PanelEditorProvider.js +33 -0
  170. package/dist/context/PanelEditorProvider/PanelEditorProvider.js.map +1 -0
  171. package/dist/context/PanelEditorProvider/index.d.ts +3 -0
  172. package/dist/context/PanelEditorProvider/index.d.ts.map +1 -0
  173. package/dist/context/PanelEditorProvider/index.js +16 -0
  174. package/dist/context/PanelEditorProvider/index.js.map +1 -0
  175. package/dist/context/VariableProvider/VariableProvider.js +1 -1
  176. package/dist/context/VariableProvider/VariableProvider.js.map +1 -1
  177. package/dist/context/index.d.ts +1 -0
  178. package/dist/context/index.d.ts.map +1 -1
  179. package/dist/context/index.js +1 -0
  180. package/dist/context/index.js.map +1 -1
  181. package/dist/context/useDashboard.js +5 -4
  182. package/dist/context/useDashboard.js.map +1 -1
  183. package/dist/utils/panelUtils.d.ts +3 -0
  184. package/dist/utils/panelUtils.d.ts.map +1 -1
  185. package/dist/utils/panelUtils.js +3 -0
  186. package/dist/utils/panelUtils.js.map +1 -1
  187. package/dist/views/ViewDashboard/DashboardApp.d.ts +7 -6
  188. package/dist/views/ViewDashboard/DashboardApp.d.ts.map +1 -1
  189. package/dist/views/ViewDashboard/DashboardApp.js +8 -4
  190. package/dist/views/ViewDashboard/DashboardApp.js.map +1 -1
  191. package/dist/views/ViewDashboard/ViewDashboard.d.ts.map +1 -1
  192. package/dist/views/ViewDashboard/ViewDashboard.js +9 -8
  193. package/dist/views/ViewDashboard/ViewDashboard.js.map +1 -1
  194. package/package.json +8 -8
@@ -24,14 +24,16 @@ _export_star(require("./DeletePanelDialog"), exports);
24
24
  _export_star(require("./DeletePanelGroupDialog"), exports);
25
25
  _export_star(require("./DiscardChangesConfirmationDialog"), exports);
26
26
  _export_star(require("./DownloadButton"), exports);
27
- _export_star(require("./EditJsonButton"), exports);
28
27
  _export_star(require("./EditJsonDialog"), exports);
28
+ _export_star(require("./EditJsonButton"), exports);
29
29
  _export_star(require("./EmptyDashboard"), exports);
30
30
  _export_star(require("./GridLayout"), exports);
31
+ _export_star(require("./LeaveDialog"), exports);
31
32
  _export_star(require("./Panel"), exports);
32
33
  _export_star(require("./PanelDrawer"), exports);
33
34
  _export_star(require("./PanelGroupDialog"), exports);
34
35
  _export_star(require("./QuerySummaryTable"), exports);
36
+ _export_star(require("./QueryViewerDialog"), exports);
35
37
  _export_star(require("./SaveChangesConfirmationDialog"), exports);
36
38
  _export_star(require("./SaveDashboardButton"), exports);
37
39
  _export_star(require("./Variables"), exports);
@@ -50,7 +50,8 @@ const TOOLTIP_TEXT = {
50
50
  movePanel: 'Move',
51
51
  // Variable editor buttons
52
52
  refreshVariableValues: 'Refresh values',
53
- copyVariableValues: 'Copy values to clipboard'
53
+ copyVariableValues: 'Copy values to clipboard',
54
+ queryView: 'Open query view'
54
55
  };
55
56
  const ARIA_LABEL_TEXT = {
56
57
  // Group buttons
@@ -65,5 +66,6 @@ const ARIA_LABEL_TEXT = {
65
66
  duplicatePanel: (panelName)=>`duplicate panel ${panelName}`,
66
67
  deletePanel: (panelName)=>`delete panel ${panelName}`,
67
68
  showPanelActions: (panelName)=>`show panel actions for ${panelName}`,
68
- movePanel: (panelName)=>`move panel ${panelName}`
69
+ movePanel: (panelName)=>`move panel ${panelName}`,
70
+ openQueryView: (panelName)=>`open query view for panel ${panelName}`
69
71
  };
@@ -61,14 +61,15 @@ function useDashboardStore(selector) {
61
61
  return (0, _traditional.useStoreWithEqualityFn)(store, selector, _shallow.shallow);
62
62
  }
63
63
  function DashboardProvider(props) {
64
+ // Prevent calling createDashboardStore every time it rerenders
64
65
  const createDashboardStore = (0, _react.useCallback)(initStore, [
65
66
  props
66
67
  ]);
68
+ const [store] = (0, _react.useState)(createDashboardStore(props));
67
69
  // load plugin to retrieve initial spec if default panel kind is defined
68
70
  const { defaultPluginKinds } = (0, _pluginsystem.usePluginRegistry)();
69
71
  const defaultPanelKind = defaultPluginKinds?.['Panel'] ?? '';
70
72
  const { data: plugin } = (0, _pluginsystem.usePlugin)('Panel', defaultPanelKind);
71
- const [store] = (0, _react.useState)(createDashboardStore(props)); // prevent calling createDashboardStore every time it rerenders
72
73
  (0, _react.useEffect)(()=>{
73
74
  if (plugin === undefined) return;
74
75
  const defaultPanelSpec = plugin.createInitialOptions ? plugin.createInitialOptions() : {};
@@ -90,12 +91,8 @@ function DashboardProvider(props) {
90
91
  }
91
92
  function initStore(props) {
92
93
  const { initialState: { dashboardResource, isEditMode, viewPanelRef, setViewPanelRef } } = props;
93
- const { kind, metadata, spec: { display, duration, refreshInterval = _core.DEFAULT_REFRESH_INTERVAL, datasources } } = dashboardResource;
94
+ const { kind, metadata, spec: { display, duration, refreshInterval = _core.DEFAULT_REFRESH_INTERVAL, datasources, layouts = [], panels = {} } } = dashboardResource;
94
95
  const ttl = 'ttl' in dashboardResource.spec ? dashboardResource.spec.ttl : undefined;
95
- let { spec: { layouts, panels } } = dashboardResource;
96
- // Set fallbacks in case the frontend is used with a non-Perses backend
97
- layouts = layouts ?? [];
98
- panels = panels ?? {};
99
96
  const store = (0, _zustand.createStore)()((0, _immer.immer)((0, _middleware.devtools)((...args)=>{
100
97
  const [set] = args;
101
98
  return {
@@ -118,9 +115,11 @@ function initStore(props) {
118
115
  datasources,
119
116
  ttl,
120
117
  isEditMode: !!isEditMode,
121
- setEditMode: (isEditMode)=>set({
118
+ setEditMode: (isEditMode)=>{
119
+ set({
122
120
  isEditMode
123
- }),
121
+ });
122
+ },
124
123
  setDashboard: ({ kind, metadata, spec: { display, panels = {}, layouts = [], duration, refreshInterval, datasources = {} } })=>{
125
124
  set((state)=>{
126
125
  state.kind = kind;
@@ -1,4 +1,4 @@
1
- // Copyright 2024 The Perses Authors
1
+ // Copyright 2025 The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -24,12 +24,12 @@ const _jsxruntime = require("react/jsx-runtime");
24
24
  const _usequeryparams = require("use-query-params");
25
25
  const _DashboardProvider = require("./DashboardProvider");
26
26
  function DashboardProviderWithQueryParams({ children, initialState }) {
27
- const [viewPanelRef, setViewPanelRef] = (0, _usequeryparams.useQueryParam)('viewPanelRef', _usequeryparams.StringParam);
27
+ const [viewPanelRef, setViewPanelRef] = (0, _usequeryparams.useQueryParam)('viewPanelRef', _usequeryparams.JsonParam);
28
28
  return /*#__PURE__*/ (0, _jsxruntime.jsx)(_DashboardProvider.DashboardProvider, {
29
29
  initialState: {
30
+ ...initialState,
30
31
  viewPanelRef: viewPanelRef ?? undefined,
31
- setViewPanelRef: setViewPanelRef,
32
- ...initialState
32
+ setViewPanelRef: setViewPanelRef
33
33
  },
34
34
  children: children
35
35
  });
@@ -49,7 +49,7 @@ function createDuplicatePanelSlice() {
49
49
  if (matchingLayout === undefined) {
50
50
  throw new Error(`Cannot find layout for Panel with key '${panelKey}'`);
51
51
  }
52
- const dupePanelKey = (0, _panelUtils.getValidPanelKey)(panelKey, panels);
52
+ const dupePanelKey = crypto.randomUUID().replaceAll('-', '');
53
53
  state.panels[dupePanelKey] = panelToDupe;
54
54
  const duplicateLayout = {
55
55
  i: (0, _common.generateId)().toString(),
@@ -42,6 +42,7 @@ function createPanelEditorSlice() {
42
42
  }
43
43
  const editorState = {
44
44
  mode: 'update',
45
+ panelGroupItemId: panelGroupItemId,
45
46
  initialValues: {
46
47
  groupId: panelGroupItemId.panelGroupId,
47
48
  panelDefinition: panelToEdit
@@ -109,8 +110,7 @@ function createPanelEditorSlice() {
109
110
  panelDefinition: get().initialValues?.panelDefinition ?? (0, _common.createPanelDefinition)()
110
111
  },
111
112
  applyChanges: (next)=>{
112
- const name = next.panelDefinition.spec.display.name;
113
- const panelKey = (0, _utils.getValidPanelKey)(name, get().panels);
113
+ const panelKey = crypto.randomUUID().replaceAll('-', '');
114
114
  set((state)=>{
115
115
  // Add a panel
116
116
  state.panels[panelKey] = next.panelDefinition;
@@ -29,12 +29,14 @@ const createPanelGroupEditorSlice = (set, get)=>({
29
29
  mode: 'Add',
30
30
  initialValues: {
31
31
  title: '',
32
- isCollapsed: false
32
+ isCollapsed: false,
33
+ repeatVariable: ''
33
34
  },
34
35
  applyChanges (next) {
35
36
  const newGroup = (0, _panelgroupslice.createEmptyPanelGroup)();
36
37
  newGroup.title = next.title;
37
38
  newGroup.isCollapsed = next.isCollapsed;
39
+ newGroup.repeatVariable = next.repeatVariable;
38
40
  set((draft)=>{
39
41
  (0, _panelgroupslice.addPanelGroup)(draft, newGroup);
40
42
  });
@@ -60,7 +62,8 @@ const createPanelGroupEditorSlice = (set, get)=>({
60
62
  mode: 'Edit',
61
63
  initialValues: {
62
64
  title: existingGroup.title ?? '',
63
- isCollapsed: existingGroup.isCollapsed
65
+ isCollapsed: existingGroup.isCollapsed,
66
+ repeatVariable: existingGroup.repeatVariable ?? ''
64
67
  },
65
68
  applyChanges (next) {
66
69
  set((draft)=>{
@@ -70,6 +73,7 @@ const createPanelGroupEditorSlice = (set, get)=>({
70
73
  }
71
74
  group.title = next.title;
72
75
  group.isCollapsed = next.isCollapsed;
76
+ group.repeatVariable = next.repeatVariable;
73
77
  });
74
78
  },
75
79
  close () {
@@ -100,6 +100,7 @@ function convertLayoutsToPanelGroups(layouts) {
100
100
  panelGroups[panelGroupId] = {
101
101
  id: panelGroupId,
102
102
  isCollapsed: layout.spec.display?.collapse?.open === false,
103
+ repeatVariable: layout.spec.repeatVariable,
103
104
  title: layout.spec.display?.title,
104
105
  itemLayouts,
105
106
  itemPanelKeys
@@ -55,12 +55,13 @@ function getViewPanelGroupId(panelGroups, panelGroupItemId, panelRef) {
55
55
  // Find the PanelGroupItemId of a Panel from a PanelRef
56
56
  function findPanelGroupItemIdOfPanelRef(panelGroups, panelRef) {
57
57
  for (const panelGroup of Object.values(panelGroups)){
58
- const itemPanel = Object.entries(panelGroup.itemPanelKeys ?? []).find(([_, value])=>value === panelRef);
58
+ const itemPanel = Object.entries(panelGroup.itemPanelKeys ?? []).find(([_, value])=>value === panelRef.ref);
59
59
  if (itemPanel) {
60
60
  const [key] = itemPanel;
61
61
  return {
62
62
  panelGroupId: panelGroup.id,
63
- panelGroupItemLayoutId: key
63
+ panelGroupItemLayoutId: key,
64
+ repeatVariable: panelRef.repeatVariable
64
65
  };
65
66
  }
66
67
  }
@@ -73,7 +74,13 @@ function findPanelRefOfPanelGroupItemId(panelGroups, panelGroupItemId) {
73
74
  }
74
75
  const panelGroup = panelGroups[panelGroupItemId.panelGroupId];
75
76
  if (panelGroup) {
76
- return panelGroup.itemPanelKeys[panelGroupItemId.panelGroupItemLayoutId];
77
+ const panelRef = panelGroup.itemPanelKeys[panelGroupItemId.panelGroupItemLayoutId];
78
+ if (panelRef) {
79
+ return {
80
+ ref: panelRef,
81
+ repeatVariable: panelGroupItemId.repeatVariable
82
+ };
83
+ }
77
84
  }
78
85
  return undefined;
79
86
  }
@@ -0,0 +1,49 @@
1
+ // Copyright 2025 The Perses Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ "use strict";
14
+ Object.defineProperty(exports, "__esModule", {
15
+ value: true
16
+ });
17
+ function _export(target, all) {
18
+ for(var name in all)Object.defineProperty(target, name, {
19
+ enumerable: true,
20
+ get: all[name]
21
+ });
22
+ }
23
+ _export(exports, {
24
+ PanelEditorContext: function() {
25
+ return PanelEditorContext;
26
+ },
27
+ PanelEditorProvider: function() {
28
+ return PanelEditorProvider;
29
+ }
30
+ });
31
+ const _jsxruntime = require("react/jsx-runtime");
32
+ const _react = require("react");
33
+ const PanelEditorContext = /*#__PURE__*/ (0, _react.createContext)(undefined);
34
+ const PanelEditorProvider = ({ children })=>{
35
+ const [previewPanelWidth, setPreviewPanelWidth] = (0, _react.useState)(undefined);
36
+ const ctx = (0, _react.useMemo)(()=>({
37
+ preview: {
38
+ previewPanelWidth,
39
+ setPreviewPanelWidth
40
+ }
41
+ }), [
42
+ previewPanelWidth,
43
+ setPreviewPanelWidth
44
+ ]);
45
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)(PanelEditorContext.Provider, {
46
+ value: ctx,
47
+ children: children
48
+ });
49
+ };
@@ -0,0 +1,23 @@
1
+ // Copyright 2025 The Perses Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ "use strict";
14
+ Object.defineProperty(exports, "__esModule", {
15
+ value: true
16
+ });
17
+ Object.defineProperty(exports, "PanelEditorContext", {
18
+ enumerable: true,
19
+ get: function() {
20
+ return _PanelEditorProvider.PanelEditorContext;
21
+ }
22
+ });
23
+ const _PanelEditorProvider = require("./PanelEditorProvider");
@@ -362,7 +362,7 @@ function createVariableDefinitionStore({ initialVariableDefinitions = [], extern
362
362
  return (0, _utils.checkSavedDefaultVariableStatus)(get().variableDefinitions, get().variableState);
363
363
  }
364
364
  }))));
365
- return store; // TODO: @Gladorme check if we can avoid this cast
365
+ return store;
366
366
  }
367
367
  function VariableProvider({ children, initialVariableDefinitions = [], externalVariableDefinitions = [], builtinVariableDefinitions = [] }) {
368
368
  const [store] = (0, _react.useState)(()=>createVariableDefinitionStore({
@@ -18,6 +18,7 @@ _export_star(require("./DashboardProvider"), exports);
18
18
  _export_star(require("./DatasourceStoreProvider"), exports);
19
19
  _export_star(require("./VariableProvider"), exports);
20
20
  _export_star(require("./useDashboard"), exports);
21
+ _export_star(require("./PanelEditorProvider"), exports);
21
22
  function _export_star(from, to) {
22
23
  Object.keys(from).forEach(function(k) {
23
24
  if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
@@ -82,11 +82,11 @@ function convertPanelGroupsToLayouts(panelGroups, panelGroupOrder) {
82
82
  if (group === undefined) {
83
83
  throw new Error('panel group not found');
84
84
  }
85
- const { title, isCollapsed, itemLayouts, itemPanelKeys } = group;
85
+ const { title, isCollapsed, repeatVariable, itemLayouts, itemPanelKeys } = group;
86
86
  let display = undefined;
87
- if (title) {
87
+ if (title || isCollapsed !== undefined) {
88
88
  display = {
89
- title,
89
+ title: title ?? '',
90
90
  collapse: {
91
91
  open: !isCollapsed
92
92
  }
@@ -108,7 +108,8 @@ function convertPanelGroupsToLayouts(panelGroups, panelGroupOrder) {
108
108
  height: layout.h,
109
109
  content: (0, _core.createPanelRef)(panelKey)
110
110
  };
111
- })
111
+ }),
112
+ repeatVariable: repeatVariable
112
113
  }
113
114
  };
114
115
  layouts.push(layout);
@@ -28,7 +28,7 @@ const _pluginsystem = require("@perses-dev/plugin-system");
28
28
  const _components1 = require("../../components");
29
29
  const _context = require("../../context");
30
30
  const DashboardApp = (props)=>{
31
- const { dashboardResource, dashboardTitleComponent, emptyDashboardProps, onSave, onDiscard, initialVariableIsSticky, isReadonly, isVariableEnabled, isDatasourceEnabled, isCreating } = props;
31
+ const { dashboardResource, emptyDashboardProps, isReadonly, isVariableEnabled, isDatasourceEnabled, isCreating, isInitialVariableSticky, isLeavingConfirmDialogEnabled, dashboardTitleComponent, onSave, onDiscard } = props;
32
32
  const chartsTheme = (0, _components.useChartsTheme)();
33
33
  const { isEditMode, setEditMode } = (0, _context.useEditMode)();
34
34
  const { dashboard, setDashboard } = (0, _context.useDashboard)();
@@ -78,7 +78,7 @@ const DashboardApp = (props)=>{
78
78
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_components1.DashboardToolbar, {
79
79
  dashboardName: dashboardResource.metadata.name,
80
80
  dashboardTitleComponent: dashboardTitleComponent,
81
- initialVariableIsSticky: initialVariableIsSticky,
81
+ initialVariableIsSticky: isInitialVariableSticky,
82
82
  onSave: onSave,
83
83
  isReadonly: isReadonly,
84
84
  isVariableEnabled: isVariableEnabled,
@@ -116,7 +116,11 @@ const DashboardApp = (props)=>{
116
116
  isReadonly: !isEditMode,
117
117
  disableMetadataEdition: !isCreating
118
118
  }),
119
- /*#__PURE__*/ (0, _jsxruntime.jsx)(_components1.SaveChangesConfirmationDialog, {})
119
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_components1.SaveChangesConfirmationDialog, {}),
120
+ isLeavingConfirmDialogEnabled && isEditMode && /*#__PURE__*/ (0, _jsxruntime.jsx)(_components1.LeaveDialog, {
121
+ original: originalDashboard,
122
+ current: dashboard
123
+ })
120
124
  ]
121
125
  })
122
126
  ]
@@ -30,7 +30,7 @@ const _context = require("../../context");
30
30
  const _DashboardProviderWithQueryParams = require("../../context/DashboardProvider/DashboardProviderWithQueryParams");
31
31
  const _DashboardApp = require("./DashboardApp");
32
32
  function ViewDashboard(props) {
33
- const { dashboardResource, datasourceApi, externalVariableDefinitions, dashboardTitleComponent, emptyDashboardProps, onSave, onDiscard, initialVariableIsSticky, isReadonly, isVariableEnabled, isDatasourceEnabled, isEditing, isCreating, sx, ...others } = props;
33
+ const { dashboardResource, datasourceApi, externalVariableDefinitions, emptyDashboardProps, isReadonly, isVariableEnabled, isDatasourceEnabled, isEditing, isCreating, isInitialVariableSticky, isLeavingConfirmDialogEnabled, dashboardTitleComponent, onSave, onDiscard, sx, ...others } = props;
34
34
  const { spec } = dashboardResource;
35
35
  const dashboardDuration = spec.duration ?? _core.DEFAULT_DASHBOARD_DURATION;
36
36
  const dashboardRefreshInterval = spec.refreshInterval ?? _core.DEFAULT_REFRESH_INTERVAL;
@@ -80,8 +80,8 @@ function ViewDashboard(props) {
80
80
  datasourceApi: datasourceApi,
81
81
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_DashboardProviderWithQueryParams.DashboardProviderWithQueryParams, {
82
82
  initialState: {
83
- dashboardResource,
84
- isEditMode: !!isEditing
83
+ isEditMode: !!isEditing,
84
+ dashboardResource
85
85
  },
86
86
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_pluginsystem.TimeRangeProviderWithQueryParams, {
87
87
  initialTimeRange: initialTimeRange,
@@ -103,15 +103,16 @@ function ViewDashboard(props) {
103
103
  FallbackComponent: _components.ErrorAlert,
104
104
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_DashboardApp.DashboardApp, {
105
105
  dashboardResource: dashboardResource,
106
- dashboardTitleComponent: dashboardTitleComponent,
107
106
  emptyDashboardProps: emptyDashboardProps,
108
- onSave: onSave,
109
- onDiscard: onDiscard,
110
- initialVariableIsSticky: initialVariableIsSticky,
111
107
  isReadonly: isReadonly,
112
108
  isVariableEnabled: isVariableEnabled,
113
109
  isDatasourceEnabled: isDatasourceEnabled,
114
- isCreating: isCreating
110
+ isCreating: isCreating,
111
+ isInitialVariableSticky: isInitialVariableSticky,
112
+ isLeavingConfirmDialogEnabled: isLeavingConfirmDialogEnabled,
113
+ dashboardTitleComponent: dashboardTitleComponent,
114
+ onSave: onSave,
115
+ onDiscard: onDiscard
115
116
  })
116
117
  })
117
118
  })
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardStickyToolbar.d.ts","sourceRoot":"","sources":["../../../src/components/DashboardStickyToolbar/DashboardStickyToolbar.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAY,MAAM,OAAO,CAAC;AAC/C,OAAO,EAKL,OAAO,EACP,KAAK,EAIN,MAAM,eAAe,CAAC;AAMvB,UAAU,2BAA2B;IACnC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,EAAE,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;CACrB;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,2BAA2B,GAAG,YAAY,CAuEvF"}
1
+ {"version":3,"file":"DashboardStickyToolbar.d.ts","sourceRoot":"","sources":["../../../src/components/DashboardStickyToolbar/DashboardStickyToolbar.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAY,MAAM,OAAO,CAAC;AAC/C,OAAO,EAKL,OAAO,EACP,KAAK,EAIN,MAAM,eAAe,CAAC;AAMvB,UAAU,2BAA2B;IACnC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,EAAE,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;CACrB;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,2BAA2B,GAAG,YAAY,CAsEvF"}
@@ -48,8 +48,6 @@ export function DashboardStickyToolbar(props) {
48
48
  display: "flex",
49
49
  flexWrap: !isSticky && isBiggerThanMd ? 'wrap' : 'nowrap',
50
50
  maxWidth: isSticky || !isBiggerThanMd ? '100vw' : '100%',
51
- maxHeight: "150px" // Limit the vertical space used to ~3 rows of variables
52
- ,
53
51
  pt: 1,
54
52
  pl: isSticky ? 1 : 0,
55
53
  mt: isSticky && isBiggerThanMd ? 0.5 : 0,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/DashboardStickyToolbar/DashboardStickyToolbar.tsx"],"sourcesContent":["// Copyright 2024 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { ReactElement, useState } from 'react';\nimport {\n AppBar,\n Box,\n IconButton,\n Stack,\n SxProps,\n Theme,\n useMediaQuery,\n useScrollTrigger,\n useTheme,\n} from '@mui/material';\nimport PinOutline from 'mdi-material-ui/PinOutline';\nimport PinOffOutline from 'mdi-material-ui/PinOffOutline';\nimport { TimeRangeControls } from '@perses-dev/plugin-system';\nimport { VariableList } from '../Variables';\n\ninterface DashboardStickyToolbarProps {\n initialVariableIsSticky?: boolean;\n sx?: SxProps<Theme>;\n}\n\nexport function DashboardStickyToolbar(props: DashboardStickyToolbarProps): ReactElement {\n const [isPin, setIsPin] = useState(props.initialVariableIsSticky);\n\n const scrollTrigger = useScrollTrigger({ disableHysteresis: true });\n const isSticky = scrollTrigger && props.initialVariableIsSticky && isPin;\n\n const isBiggerThanMd = useMediaQuery(useTheme().breakpoints.up('md'));\n\n return (\n // marginBottom={-1} counteracts the marginBottom={1} on every variable input.\n // The margin on the inputs is for spacing between inputs, but is not meant to add space to bottom of the container.\n <Box marginBottom={-1} data-testid=\"variable-list\">\n <AppBar\n color=\"inherit\"\n position={isSticky ? 'fixed' : 'static'}\n elevation={isSticky ? 4 : 0}\n sx={{ backgroundColor: 'inherit', ...props.sx }}\n >\n <Box\n display=\"flex\"\n justifyContent=\"space-between\"\n sx={{\n flexDirection: isBiggerThanMd ? 'row' : 'column',\n }}\n >\n <Box\n display=\"flex\"\n flexWrap={!isSticky && isBiggerThanMd ? 'wrap' : 'nowrap'}\n maxWidth={isSticky || !isBiggerThanMd ? '100vw' : '100%'}\n maxHeight=\"150px\" // Limit the vertical space used to ~3 rows of variables\n pt={1}\n pl={isSticky ? 1 : 0}\n mt={isSticky && isBiggerThanMd ? 0.5 : 0}\n ml={isSticky && isBiggerThanMd ? 0.5 : 0}\n sx={{\n overflowX: !isSticky && isBiggerThanMd ? 'hidden' : 'auto',\n // Firefox:\n scrollbarWidth: 'thin',\n // Safari and Chrome:\n '&::-webkit-scrollbar': {\n height: '8px',\n backgroundColor: (theme) => theme.palette.grey['300'],\n },\n '&::-webkit-scrollbar-thumb': {\n background: (theme) => theme.palette.grey['600'],\n },\n }}\n gap={1}\n >\n <VariableList />\n {props.initialVariableIsSticky && (\n <IconButton style={{ width: 'fit-content', height: 'fit-content' }} onClick={() => setIsPin(!isPin)}>\n {isPin ? <PinOutline /> : <PinOffOutline />}\n </IconButton>\n )}\n </Box>\n {isSticky && (\n <Stack\n m={isBiggerThanMd ? 1.5 : 1}\n mt={isBiggerThanMd ? 1.5 : 0}\n ml={isBiggerThanMd ? 1.5 : 'auto'}\n direction=\"row\"\n justifyContent=\"end\"\n >\n <TimeRangeControls />\n </Stack>\n )}\n </Box>\n </AppBar>\n </Box>\n );\n}\n"],"names":["useState","AppBar","Box","IconButton","Stack","useMediaQuery","useScrollTrigger","useTheme","PinOutline","PinOffOutline","TimeRangeControls","VariableList","DashboardStickyToolbar","props","isPin","setIsPin","initialVariableIsSticky","scrollTrigger","disableHysteresis","isSticky","isBiggerThanMd","breakpoints","up","marginBottom","data-testid","color","position","elevation","sx","backgroundColor","display","justifyContent","flexDirection","flexWrap","maxWidth","maxHeight","pt","pl","mt","ml","overflowX","scrollbarWidth","height","theme","palette","grey","background","gap","style","width","onClick","m","direction"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAEjC,SAAuBA,QAAQ,QAAQ,QAAQ;AAC/C,SACEC,MAAM,EACNC,GAAG,EACHC,UAAU,EACVC,KAAK,EAGLC,aAAa,EACbC,gBAAgB,EAChBC,QAAQ,QACH,gBAAgB;AACvB,OAAOC,gBAAgB,6BAA6B;AACpD,OAAOC,mBAAmB,gCAAgC;AAC1D,SAASC,iBAAiB,QAAQ,4BAA4B;AAC9D,SAASC,YAAY,QAAQ,eAAe;AAO5C,OAAO,SAASC,uBAAuBC,KAAkC;IACvE,MAAM,CAACC,OAAOC,SAAS,GAAGf,SAASa,MAAMG,uBAAuB;IAEhE,MAAMC,gBAAgBX,iBAAiB;QAAEY,mBAAmB;IAAK;IACjE,MAAMC,WAAWF,iBAAiBJ,MAAMG,uBAAuB,IAAIF;IAEnE,MAAMM,iBAAiBf,cAAcE,WAAWc,WAAW,CAACC,EAAE,CAAC;IAE/D,OACE,8EAA8E;IAC9E,oHAAoH;kBACpH,KAACpB;QAAIqB,cAAc,CAAC;QAAGC,eAAY;kBACjC,cAAA,KAACvB;YACCwB,OAAM;YACNC,UAAUP,WAAW,UAAU;YAC/BQ,WAAWR,WAAW,IAAI;YAC1BS,IAAI;gBAAEC,iBAAiB;gBAAW,GAAGhB,MAAMe,EAAE;YAAC;sBAE9C,cAAA,MAAC1B;gBACC4B,SAAQ;gBACRC,gBAAe;gBACfH,IAAI;oBACFI,eAAeZ,iBAAiB,QAAQ;gBAC1C;;kCAEA,MAAClB;wBACC4B,SAAQ;wBACRG,UAAU,CAACd,YAAYC,iBAAiB,SAAS;wBACjDc,UAAUf,YAAY,CAACC,iBAAiB,UAAU;wBAClDe,WAAU,QAAQ,wDAAwD;;wBAC1EC,IAAI;wBACJC,IAAIlB,WAAW,IAAI;wBACnBmB,IAAInB,YAAYC,iBAAiB,MAAM;wBACvCmB,IAAIpB,YAAYC,iBAAiB,MAAM;wBACvCQ,IAAI;4BACFY,WAAW,CAACrB,YAAYC,iBAAiB,WAAW;4BACpD,WAAW;4BACXqB,gBAAgB;4BAChB,qBAAqB;4BACrB,wBAAwB;gCACtBC,QAAQ;gCACRb,iBAAiB,CAACc,QAAUA,MAAMC,OAAO,CAACC,IAAI,CAAC,MAAM;4BACvD;4BACA,8BAA8B;gCAC5BC,YAAY,CAACH,QAAUA,MAAMC,OAAO,CAACC,IAAI,CAAC,MAAM;4BAClD;wBACF;wBACAE,KAAK;;0CAEL,KAACpC;4BACAE,MAAMG,uBAAuB,kBAC5B,KAACb;gCAAW6C,OAAO;oCAAEC,OAAO;oCAAeP,QAAQ;gCAAc;gCAAGQ,SAAS,IAAMnC,SAAS,CAACD;0CAC1FA,sBAAQ,KAACN,gCAAgB,KAACC;;;;oBAIhCU,0BACC,KAACf;wBACC+C,GAAG/B,iBAAiB,MAAM;wBAC1BkB,IAAIlB,iBAAiB,MAAM;wBAC3BmB,IAAInB,iBAAiB,MAAM;wBAC3BgC,WAAU;wBACVrB,gBAAe;kCAEf,cAAA,KAACrB;;;;;;AAOf"}
1
+ {"version":3,"sources":["../../../src/components/DashboardStickyToolbar/DashboardStickyToolbar.tsx"],"sourcesContent":["// Copyright 2024 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { ReactElement, useState } from 'react';\nimport {\n AppBar,\n Box,\n IconButton,\n Stack,\n SxProps,\n Theme,\n useMediaQuery,\n useScrollTrigger,\n useTheme,\n} from '@mui/material';\nimport PinOutline from 'mdi-material-ui/PinOutline';\nimport PinOffOutline from 'mdi-material-ui/PinOffOutline';\nimport { TimeRangeControls } from '@perses-dev/plugin-system';\nimport { VariableList } from '../Variables';\n\ninterface DashboardStickyToolbarProps {\n initialVariableIsSticky?: boolean;\n sx?: SxProps<Theme>;\n}\n\nexport function DashboardStickyToolbar(props: DashboardStickyToolbarProps): ReactElement {\n const [isPin, setIsPin] = useState(props.initialVariableIsSticky);\n\n const scrollTrigger = useScrollTrigger({ disableHysteresis: true });\n const isSticky = scrollTrigger && props.initialVariableIsSticky && isPin;\n\n const isBiggerThanMd = useMediaQuery(useTheme().breakpoints.up('md'));\n\n return (\n // marginBottom={-1} counteracts the marginBottom={1} on every variable input.\n // The margin on the inputs is for spacing between inputs, but is not meant to add space to bottom of the container.\n <Box marginBottom={-1} data-testid=\"variable-list\">\n <AppBar\n color=\"inherit\"\n position={isSticky ? 'fixed' : 'static'}\n elevation={isSticky ? 4 : 0}\n sx={{ backgroundColor: 'inherit', ...props.sx }}\n >\n <Box\n display=\"flex\"\n justifyContent=\"space-between\"\n sx={{\n flexDirection: isBiggerThanMd ? 'row' : 'column',\n }}\n >\n <Box\n display=\"flex\"\n flexWrap={!isSticky && isBiggerThanMd ? 'wrap' : 'nowrap'}\n maxWidth={isSticky || !isBiggerThanMd ? '100vw' : '100%'}\n pt={1}\n pl={isSticky ? 1 : 0}\n mt={isSticky && isBiggerThanMd ? 0.5 : 0}\n ml={isSticky && isBiggerThanMd ? 0.5 : 0}\n sx={{\n overflowX: !isSticky && isBiggerThanMd ? 'hidden' : 'auto',\n // Firefox:\n scrollbarWidth: 'thin',\n // Safari and Chrome:\n '&::-webkit-scrollbar': {\n height: '8px',\n backgroundColor: (theme) => theme.palette.grey['300'],\n },\n '&::-webkit-scrollbar-thumb': {\n background: (theme) => theme.palette.grey['600'],\n },\n }}\n gap={1}\n >\n <VariableList />\n {props.initialVariableIsSticky && (\n <IconButton style={{ width: 'fit-content', height: 'fit-content' }} onClick={() => setIsPin(!isPin)}>\n {isPin ? <PinOutline /> : <PinOffOutline />}\n </IconButton>\n )}\n </Box>\n {isSticky && (\n <Stack\n m={isBiggerThanMd ? 1.5 : 1}\n mt={isBiggerThanMd ? 1.5 : 0}\n ml={isBiggerThanMd ? 1.5 : 'auto'}\n direction=\"row\"\n justifyContent=\"end\"\n >\n <TimeRangeControls />\n </Stack>\n )}\n </Box>\n </AppBar>\n </Box>\n );\n}\n"],"names":["useState","AppBar","Box","IconButton","Stack","useMediaQuery","useScrollTrigger","useTheme","PinOutline","PinOffOutline","TimeRangeControls","VariableList","DashboardStickyToolbar","props","isPin","setIsPin","initialVariableIsSticky","scrollTrigger","disableHysteresis","isSticky","isBiggerThanMd","breakpoints","up","marginBottom","data-testid","color","position","elevation","sx","backgroundColor","display","justifyContent","flexDirection","flexWrap","maxWidth","pt","pl","mt","ml","overflowX","scrollbarWidth","height","theme","palette","grey","background","gap","style","width","onClick","m","direction"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAEjC,SAAuBA,QAAQ,QAAQ,QAAQ;AAC/C,SACEC,MAAM,EACNC,GAAG,EACHC,UAAU,EACVC,KAAK,EAGLC,aAAa,EACbC,gBAAgB,EAChBC,QAAQ,QACH,gBAAgB;AACvB,OAAOC,gBAAgB,6BAA6B;AACpD,OAAOC,mBAAmB,gCAAgC;AAC1D,SAASC,iBAAiB,QAAQ,4BAA4B;AAC9D,SAASC,YAAY,QAAQ,eAAe;AAO5C,OAAO,SAASC,uBAAuBC,KAAkC;IACvE,MAAM,CAACC,OAAOC,SAAS,GAAGf,SAASa,MAAMG,uBAAuB;IAEhE,MAAMC,gBAAgBX,iBAAiB;QAAEY,mBAAmB;IAAK;IACjE,MAAMC,WAAWF,iBAAiBJ,MAAMG,uBAAuB,IAAIF;IAEnE,MAAMM,iBAAiBf,cAAcE,WAAWc,WAAW,CAACC,EAAE,CAAC;IAE/D,OACE,8EAA8E;IAC9E,oHAAoH;kBACpH,KAACpB;QAAIqB,cAAc,CAAC;QAAGC,eAAY;kBACjC,cAAA,KAACvB;YACCwB,OAAM;YACNC,UAAUP,WAAW,UAAU;YAC/BQ,WAAWR,WAAW,IAAI;YAC1BS,IAAI;gBAAEC,iBAAiB;gBAAW,GAAGhB,MAAMe,EAAE;YAAC;sBAE9C,cAAA,MAAC1B;gBACC4B,SAAQ;gBACRC,gBAAe;gBACfH,IAAI;oBACFI,eAAeZ,iBAAiB,QAAQ;gBAC1C;;kCAEA,MAAClB;wBACC4B,SAAQ;wBACRG,UAAU,CAACd,YAAYC,iBAAiB,SAAS;wBACjDc,UAAUf,YAAY,CAACC,iBAAiB,UAAU;wBAClDe,IAAI;wBACJC,IAAIjB,WAAW,IAAI;wBACnBkB,IAAIlB,YAAYC,iBAAiB,MAAM;wBACvCkB,IAAInB,YAAYC,iBAAiB,MAAM;wBACvCQ,IAAI;4BACFW,WAAW,CAACpB,YAAYC,iBAAiB,WAAW;4BACpD,WAAW;4BACXoB,gBAAgB;4BAChB,qBAAqB;4BACrB,wBAAwB;gCACtBC,QAAQ;gCACRZ,iBAAiB,CAACa,QAAUA,MAAMC,OAAO,CAACC,IAAI,CAAC,MAAM;4BACvD;4BACA,8BAA8B;gCAC5BC,YAAY,CAACH,QAAUA,MAAMC,OAAO,CAACC,IAAI,CAAC,MAAM;4BAClD;wBACF;wBACAE,KAAK;;0CAEL,KAACnC;4BACAE,MAAMG,uBAAuB,kBAC5B,KAACb;gCAAW4C,OAAO;oCAAEC,OAAO;oCAAeP,QAAQ;gCAAc;gCAAGQ,SAAS,IAAMlC,SAAS,CAACD;0CAC1FA,sBAAQ,KAACN,gCAAgB,KAACC;;;;oBAIhCU,0BACC,KAACf;wBACC8C,GAAG9B,iBAAiB,MAAM;wBAC1BiB,IAAIjB,iBAAiB,MAAM;wBAC3BkB,IAAIlB,iBAAiB,MAAM;wBAC3B+B,WAAU;wBACVpB,gBAAe;kCAEf,cAAA,KAACrB;;;;;;AAOf"}
@@ -1,8 +1,8 @@
1
- import { ReactElement } from 'react';
1
+ import { ReactElement, ReactNode } from 'react';
2
2
  import { OnSaveDashboard } from '../../context';
3
3
  export interface DashboardToolbarProps {
4
4
  dashboardName: string;
5
- dashboardTitleComponent?: JSX.Element;
5
+ dashboardTitleComponent?: ReactNode;
6
6
  initialVariableIsSticky?: boolean;
7
7
  isReadonly: boolean;
8
8
  isVariableEnabled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardToolbar.d.ts","sourceRoot":"","sources":["../../../src/components/DashboardToolbar/DashboardToolbar.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AACrC,OAAO,EAAE,eAAe,EAAe,MAAM,eAAe,CAAC;AAW7D,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACtC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,EAAE,MAAM,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED,eAAO,MAAM,gBAAgB,UAAW,qBAAqB,KAAG,YAgG/D,CAAC"}
1
+ {"version":3,"file":"DashboardToolbar.d.ts","sourceRoot":"","sources":["../../../src/components/DashboardToolbar/DashboardToolbar.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAChD,OAAO,EAAE,eAAe,EAAe,MAAM,eAAe,CAAC;AAW7D,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,CAAC,EAAE,SAAS,CAAC;IACpC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,EAAE,MAAM,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED,eAAO,MAAM,gBAAgB,UAAW,qBAAqB,KAAG,YAgG/D,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/DashboardToolbar/DashboardToolbar.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { Typography, Stack, Button, Box, useTheme, useMediaQuery, Alert } from '@mui/material';\nimport { ErrorBoundary, ErrorAlert } from '@perses-dev/components';\nimport { TimeRangeControls } from '@perses-dev/plugin-system';\nimport { ReactElement } from 'react';\nimport { OnSaveDashboard, useEditMode } from '../../context';\nimport { AddPanelButton } from '../AddPanelButton';\nimport { AddGroupButton } from '../AddGroupButton';\nimport { DownloadButton } from '../DownloadButton';\nimport { EditVariablesButton } from '../Variables';\nimport { EditDatasourcesButton } from '../Datasources';\nimport { EditButton } from '../EditButton';\nimport { EditJsonButton } from '../EditJsonButton';\nimport { SaveDashboardButton } from '../SaveDashboardButton';\nimport { DashboardStickyToolbar } from '../DashboardStickyToolbar';\n\nexport interface DashboardToolbarProps {\n dashboardName: string;\n dashboardTitleComponent?: JSX.Element;\n initialVariableIsSticky?: boolean;\n isReadonly: boolean;\n isVariableEnabled: boolean;\n isDatasourceEnabled: boolean;\n onEditButtonClick: () => void;\n onCancelButtonClick: () => void;\n onSave?: OnSaveDashboard;\n}\n\nexport const DashboardToolbar = (props: DashboardToolbarProps): ReactElement => {\n const {\n dashboardName,\n dashboardTitleComponent,\n initialVariableIsSticky,\n isReadonly,\n isVariableEnabled,\n isDatasourceEnabled,\n onEditButtonClick,\n onCancelButtonClick,\n onSave,\n } = props;\n\n const { isEditMode } = useEditMode();\n\n const isBiggerThanSm = useMediaQuery(useTheme().breakpoints.up('sm'));\n const isBiggerThanMd = useMediaQuery(useTheme().breakpoints.up('md'));\n\n const dashboardTitle = dashboardTitleComponent ? (\n dashboardTitleComponent\n ) : (\n <Typography variant=\"h2\">{dashboardName}</Typography>\n );\n\n const testId = 'dashboard-toolbar';\n\n return (\n <>\n <Stack data-testid={testId}>\n <Box\n px={2}\n py={1.5}\n display=\"flex\"\n sx={{ backgroundColor: (theme) => theme.palette.primary.main + (isEditMode ? '30' : '0') }}\n >\n {dashboardTitle}\n {isEditMode ? (\n <Stack direction=\"row\" gap={1} ml=\"auto\">\n {isReadonly && (\n <Alert severity=\"warning\" sx={{ backgroundColor: 'transparent', padding: 0 }}>\n Dashboard managed via code only. Download JSON and commit changes to save.\n </Alert>\n )}\n <Stack direction=\"row\" spacing={0.5} ml={1} whiteSpace=\"nowrap\">\n {isVariableEnabled && <EditVariablesButton />}\n {isDatasourceEnabled && <EditDatasourcesButton />}\n <AddPanelButton />\n <AddGroupButton />\n </Stack>\n <SaveDashboardButton onSave={onSave} isDisabled={isReadonly} />\n <Button variant=\"outlined\" onClick={onCancelButtonClick}>\n Cancel\n </Button>\n </Stack>\n ) : (\n <>\n {isBiggerThanSm && (\n <Stack direction=\"row\" gap={1} ml=\"auto\">\n <EditButton onClick={onEditButtonClick} />\n </Stack>\n )}\n </>\n )}\n </Box>\n <Box\n sx={{\n display: 'flex',\n width: '100%',\n alignItems: 'start',\n padding: (theme) => theme.spacing(1, 2, 0, 2),\n flexDirection: isBiggerThanMd ? 'row' : 'column',\n flexWrap: 'nowrap',\n gap: 1,\n }}\n >\n <Box width=\"100%\">\n <ErrorBoundary FallbackComponent={ErrorAlert}>\n <DashboardStickyToolbar\n initialVariableIsSticky={initialVariableIsSticky}\n sx={{\n backgroundColor: ({ palette }) => palette.background.default,\n }}\n />\n </ErrorBoundary>\n </Box>\n <Stack direction=\"row\" ml=\"auto\" flexWrap=\"wrap\" justifyContent=\"end\">\n <Stack direction=\"row\" spacing={1} mt={1} ml={1}>\n <TimeRangeControls />\n <DownloadButton />\n <EditJsonButton isReadonly={!isEditMode} />\n </Stack>\n </Stack>\n </Box>\n </Stack>\n </>\n );\n};\n"],"names":["Typography","Stack","Button","Box","useTheme","useMediaQuery","Alert","ErrorBoundary","ErrorAlert","TimeRangeControls","useEditMode","AddPanelButton","AddGroupButton","DownloadButton","EditVariablesButton","EditDatasourcesButton","EditButton","EditJsonButton","SaveDashboardButton","DashboardStickyToolbar","DashboardToolbar","props","dashboardName","dashboardTitleComponent","initialVariableIsSticky","isReadonly","isVariableEnabled","isDatasourceEnabled","onEditButtonClick","onCancelButtonClick","onSave","isEditMode","isBiggerThanSm","breakpoints","up","isBiggerThanMd","dashboardTitle","variant","testId","data-testid","px","py","display","sx","backgroundColor","theme","palette","primary","main","direction","gap","ml","severity","padding","spacing","whiteSpace","isDisabled","onClick","width","alignItems","flexDirection","flexWrap","FallbackComponent","background","default","justifyContent","mt"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/DashboardToolbar/DashboardToolbar.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { Typography, Stack, Button, Box, useTheme, useMediaQuery, Alert } from '@mui/material';\nimport { ErrorBoundary, ErrorAlert } from '@perses-dev/components';\nimport { TimeRangeControls } from '@perses-dev/plugin-system';\nimport { ReactElement, ReactNode } from 'react';\nimport { OnSaveDashboard, useEditMode } from '../../context';\nimport { AddPanelButton } from '../AddPanelButton';\nimport { AddGroupButton } from '../AddGroupButton';\nimport { DownloadButton } from '../DownloadButton';\nimport { EditVariablesButton } from '../Variables';\nimport { EditDatasourcesButton } from '../Datasources';\nimport { EditButton } from '../EditButton';\nimport { EditJsonButton } from '../EditJsonButton';\nimport { SaveDashboardButton } from '../SaveDashboardButton';\nimport { DashboardStickyToolbar } from '../DashboardStickyToolbar';\n\nexport interface DashboardToolbarProps {\n dashboardName: string;\n dashboardTitleComponent?: ReactNode;\n initialVariableIsSticky?: boolean;\n isReadonly: boolean;\n isVariableEnabled: boolean;\n isDatasourceEnabled: boolean;\n onEditButtonClick: () => void;\n onCancelButtonClick: () => void;\n onSave?: OnSaveDashboard;\n}\n\nexport const DashboardToolbar = (props: DashboardToolbarProps): ReactElement => {\n const {\n dashboardName,\n dashboardTitleComponent,\n initialVariableIsSticky,\n isReadonly,\n isVariableEnabled,\n isDatasourceEnabled,\n onEditButtonClick,\n onCancelButtonClick,\n onSave,\n } = props;\n\n const { isEditMode } = useEditMode();\n\n const isBiggerThanSm = useMediaQuery(useTheme().breakpoints.up('sm'));\n const isBiggerThanMd = useMediaQuery(useTheme().breakpoints.up('md'));\n\n const dashboardTitle = dashboardTitleComponent ? (\n dashboardTitleComponent\n ) : (\n <Typography variant=\"h2\">{dashboardName}</Typography>\n );\n\n const testId = 'dashboard-toolbar';\n\n return (\n <>\n <Stack data-testid={testId}>\n <Box\n px={2}\n py={1.5}\n display=\"flex\"\n sx={{ backgroundColor: (theme) => theme.palette.primary.main + (isEditMode ? '30' : '0') }}\n >\n {dashboardTitle}\n {isEditMode ? (\n <Stack direction=\"row\" gap={1} ml=\"auto\">\n {isReadonly && (\n <Alert severity=\"warning\" sx={{ backgroundColor: 'transparent', padding: 0 }}>\n Dashboard managed via code only. Download JSON and commit changes to save.\n </Alert>\n )}\n <Stack direction=\"row\" spacing={0.5} ml={1} whiteSpace=\"nowrap\">\n {isVariableEnabled && <EditVariablesButton />}\n {isDatasourceEnabled && <EditDatasourcesButton />}\n <AddPanelButton />\n <AddGroupButton />\n </Stack>\n <SaveDashboardButton onSave={onSave} isDisabled={isReadonly} />\n <Button variant=\"outlined\" onClick={onCancelButtonClick}>\n Cancel\n </Button>\n </Stack>\n ) : (\n <>\n {isBiggerThanSm && (\n <Stack direction=\"row\" gap={1} ml=\"auto\">\n <EditButton onClick={onEditButtonClick} />\n </Stack>\n )}\n </>\n )}\n </Box>\n <Box\n sx={{\n display: 'flex',\n width: '100%',\n alignItems: 'start',\n padding: (theme) => theme.spacing(1, 2, 0, 2),\n flexDirection: isBiggerThanMd ? 'row' : 'column',\n flexWrap: 'nowrap',\n gap: 1,\n }}\n >\n <Box width=\"100%\">\n <ErrorBoundary FallbackComponent={ErrorAlert}>\n <DashboardStickyToolbar\n initialVariableIsSticky={initialVariableIsSticky}\n sx={{\n backgroundColor: ({ palette }) => palette.background.default,\n }}\n />\n </ErrorBoundary>\n </Box>\n <Stack direction=\"row\" ml=\"auto\" flexWrap=\"wrap\" justifyContent=\"end\">\n <Stack direction=\"row\" spacing={1} mt={1} ml={1}>\n <TimeRangeControls />\n <DownloadButton />\n <EditJsonButton isReadonly={!isEditMode} />\n </Stack>\n </Stack>\n </Box>\n </Stack>\n </>\n );\n};\n"],"names":["Typography","Stack","Button","Box","useTheme","useMediaQuery","Alert","ErrorBoundary","ErrorAlert","TimeRangeControls","useEditMode","AddPanelButton","AddGroupButton","DownloadButton","EditVariablesButton","EditDatasourcesButton","EditButton","EditJsonButton","SaveDashboardButton","DashboardStickyToolbar","DashboardToolbar","props","dashboardName","dashboardTitleComponent","initialVariableIsSticky","isReadonly","isVariableEnabled","isDatasourceEnabled","onEditButtonClick","onCancelButtonClick","onSave","isEditMode","isBiggerThanSm","breakpoints","up","isBiggerThanMd","dashboardTitle","variant","testId","data-testid","px","py","display","sx","backgroundColor","theme","palette","primary","main","direction","gap","ml","severity","padding","spacing","whiteSpace","isDisabled","onClick","width","alignItems","flexDirection","flexWrap","FallbackComponent","background","default","justifyContent","mt"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DeletePanelGroupDialog.d.ts","sourceRoot":"","sources":["../../../src/components/DeletePanelGroupDialog/DeletePanelGroupDialog.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAa,YAAY,EAAE,MAAM,OAAO,CAAC;AAKhD,eAAO,MAAM,sBAAsB,QAAO,YA6CzC,CAAC"}
1
+ {"version":3,"file":"DeletePanelGroupDialog.d.ts","sourceRoot":"","sources":["../../../src/components/DeletePanelGroupDialog/DeletePanelGroupDialog.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAa,YAAY,EAAE,MAAM,OAAO,CAAC;AAKhD,eAAO,MAAM,sBAAsB,QAAO,YAmCzC,CAAC"}
@@ -11,8 +11,8 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
- import { IconButton, Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
15
- import CloseIcon from 'mdi-material-ui/Close';
14
+ import { Dialog } from '@perses-dev/components';
15
+ import { Button } from '@mui/material';
16
16
  import { useDeletePanelGroupDialog, useViewPanel } from '../../context';
17
17
  export const DeletePanelGroupDialog = ()=>{
18
18
  const { deletePanelGroupDialog, closeDeletePanelGroupDialog, deletePanelGroup } = useDeletePanelGroupDialog();
@@ -30,23 +30,13 @@ export const DeletePanelGroupDialog = ()=>{
30
30
  return /*#__PURE__*/ _jsxs(Dialog, {
31
31
  open: deletePanelGroupDialog !== undefined,
32
32
  children: [
33
- /*#__PURE__*/ _jsx(DialogTitle, {
33
+ /*#__PURE__*/ _jsx(Dialog.Header, {
34
34
  children: "Delete Panel Group"
35
35
  }),
36
- /*#__PURE__*/ _jsx(IconButton, {
37
- "aria-label": "Close",
38
- onClick: ()=>closeDeletePanelGroupDialog(),
39
- sx: (theme)=>({
40
- position: 'absolute',
41
- top: theme.spacing(0.5),
42
- right: theme.spacing(0.5)
43
- }),
44
- children: /*#__PURE__*/ _jsx(CloseIcon, {})
45
- }),
46
36
  /*#__PURE__*/ _jsxs("form", {
47
37
  onSubmit: handleDelete,
48
38
  children: [
49
- /*#__PURE__*/ _jsxs(DialogContent, {
39
+ /*#__PURE__*/ _jsxs(Dialog.Content, {
50
40
  dividers: true,
51
41
  sx: {
52
42
  width: '500px'
@@ -57,7 +47,7 @@ export const DeletePanelGroupDialog = ()=>{
57
47
  "? This will delete all the panels within the group."
58
48
  ]
59
49
  }),
60
- /*#__PURE__*/ _jsxs(DialogActions, {
50
+ /*#__PURE__*/ _jsxs(Dialog.Actions, {
61
51
  children: [
62
52
  /*#__PURE__*/ _jsx(Button, {
63
53
  variant: "contained",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/DeletePanelGroupDialog/DeletePanelGroupDialog.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { FormEvent, ReactElement } from 'react';\nimport { IconButton, Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';\nimport CloseIcon from 'mdi-material-ui/Close';\nimport { useDeletePanelGroupDialog, useViewPanel } from '../../context';\n\nexport const DeletePanelGroupDialog = (): ReactElement => {\n const { deletePanelGroupDialog, closeDeletePanelGroupDialog, deletePanelGroup } = useDeletePanelGroupDialog();\n const { setViewPanel } = useViewPanel();\n const panelGroupId = deletePanelGroupDialog?.panelGroupId;\n\n const handleDelete = (e: FormEvent): void => {\n e.preventDefault();\n if (panelGroupId === undefined) {\n throw new Error('group index is undefined');\n }\n deletePanelGroup(panelGroupId);\n closeDeletePanelGroupDialog();\n setViewPanel(undefined);\n };\n\n return (\n <Dialog open={deletePanelGroupDialog !== undefined}>\n <DialogTitle>Delete Panel Group</DialogTitle>\n <IconButton\n aria-label=\"Close\"\n onClick={() => closeDeletePanelGroupDialog()}\n sx={(theme) => ({\n position: 'absolute',\n top: theme.spacing(0.5),\n right: theme.spacing(0.5),\n })}\n >\n <CloseIcon />\n </IconButton>\n <form onSubmit={handleDelete}>\n <DialogContent dividers sx={{ width: '500px' }}>\n Are you sure you want to delete {deletePanelGroupDialog?.panelGroupName ?? 'panel group'}? This will delete\n all the panels within the group.\n </DialogContent>\n <DialogActions>\n <Button variant=\"contained\" type=\"submit\">\n Delete\n </Button>\n <Button variant=\"outlined\" color=\"secondary\" onClick={() => closeDeletePanelGroupDialog()}>\n Cancel\n </Button>\n </DialogActions>\n </form>\n </Dialog>\n );\n};\n"],"names":["IconButton","Dialog","DialogTitle","DialogContent","DialogActions","Button","CloseIcon","useDeletePanelGroupDialog","useViewPanel","DeletePanelGroupDialog","deletePanelGroupDialog","closeDeletePanelGroupDialog","deletePanelGroup","setViewPanel","panelGroupId","handleDelete","e","preventDefault","undefined","Error","open","aria-label","onClick","sx","theme","position","top","spacing","right","form","onSubmit","dividers","width","panelGroupName","variant","type","color"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAGjC,SAASA,UAAU,EAAEC,MAAM,EAAEC,WAAW,EAAEC,aAAa,EAAEC,aAAa,EAAEC,MAAM,QAAQ,gBAAgB;AACtG,OAAOC,eAAe,wBAAwB;AAC9C,SAASC,yBAAyB,EAAEC,YAAY,QAAQ,gBAAgB;AAExE,OAAO,MAAMC,yBAAyB;IACpC,MAAM,EAAEC,sBAAsB,EAAEC,2BAA2B,EAAEC,gBAAgB,EAAE,GAAGL;IAClF,MAAM,EAAEM,YAAY,EAAE,GAAGL;IACzB,MAAMM,eAAeJ,wBAAwBI;IAE7C,MAAMC,eAAe,CAACC;QACpBA,EAAEC,cAAc;QAChB,IAAIH,iBAAiBI,WAAW;YAC9B,MAAM,IAAIC,MAAM;QAClB;QACAP,iBAAiBE;QACjBH;QACAE,aAAaK;IACf;IAEA,qBACE,MAACjB;QAAOmB,MAAMV,2BAA2BQ;;0BACvC,KAAChB;0BAAY;;0BACb,KAACF;gBACCqB,cAAW;gBACXC,SAAS,IAAMX;gBACfY,IAAI,CAACC,QAAW,CAAA;wBACdC,UAAU;wBACVC,KAAKF,MAAMG,OAAO,CAAC;wBACnBC,OAAOJ,MAAMG,OAAO,CAAC;oBACvB,CAAA;0BAEA,cAAA,KAACrB;;0BAEH,MAACuB;gBAAKC,UAAUf;;kCACd,MAACZ;wBAAc4B,QAAQ;wBAACR,IAAI;4BAAES,OAAO;wBAAQ;;4BAAG;4BACbtB,wBAAwBuB,kBAAkB;4BAAc;;;kCAG3F,MAAC7B;;0CACC,KAACC;gCAAO6B,SAAQ;gCAAYC,MAAK;0CAAS;;0CAG1C,KAAC9B;gCAAO6B,SAAQ;gCAAWE,OAAM;gCAAYd,SAAS,IAAMX;0CAA+B;;;;;;;;AAOrG,EAAE"}
1
+ {"version":3,"sources":["../../../src/components/DeletePanelGroupDialog/DeletePanelGroupDialog.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { FormEvent, ReactElement } from 'react';\nimport { Dialog } from '@perses-dev/components';\nimport { Button } from '@mui/material';\nimport { useDeletePanelGroupDialog, useViewPanel } from '../../context';\n\nexport const DeletePanelGroupDialog = (): ReactElement => {\n const { deletePanelGroupDialog, closeDeletePanelGroupDialog, deletePanelGroup } = useDeletePanelGroupDialog();\n const { setViewPanel } = useViewPanel();\n const panelGroupId = deletePanelGroupDialog?.panelGroupId;\n\n const handleDelete = (e: FormEvent): void => {\n e.preventDefault();\n if (panelGroupId === undefined) {\n throw new Error('group index is undefined');\n }\n deletePanelGroup(panelGroupId);\n closeDeletePanelGroupDialog();\n setViewPanel(undefined);\n };\n\n return (\n <Dialog open={deletePanelGroupDialog !== undefined}>\n <Dialog.Header>Delete Panel Group</Dialog.Header>\n\n <form onSubmit={handleDelete}>\n <Dialog.Content dividers sx={{ width: '500px' }}>\n Are you sure you want to delete {deletePanelGroupDialog?.panelGroupName ?? 'panel group'}? This will delete\n all the panels within the group.\n </Dialog.Content>\n <Dialog.Actions>\n <Button variant=\"contained\" type=\"submit\">\n Delete\n </Button>\n <Button variant=\"outlined\" color=\"secondary\" onClick={() => closeDeletePanelGroupDialog()}>\n Cancel\n </Button>\n </Dialog.Actions>\n </form>\n </Dialog>\n );\n};\n"],"names":["Dialog","Button","useDeletePanelGroupDialog","useViewPanel","DeletePanelGroupDialog","deletePanelGroupDialog","closeDeletePanelGroupDialog","deletePanelGroup","setViewPanel","panelGroupId","handleDelete","e","preventDefault","undefined","Error","open","Header","form","onSubmit","Content","dividers","sx","width","panelGroupName","Actions","variant","type","color","onClick"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAGjC,SAASA,MAAM,QAAQ,yBAAyB;AAChD,SAASC,MAAM,QAAQ,gBAAgB;AACvC,SAASC,yBAAyB,EAAEC,YAAY,QAAQ,gBAAgB;AAExE,OAAO,MAAMC,yBAAyB;IACpC,MAAM,EAAEC,sBAAsB,EAAEC,2BAA2B,EAAEC,gBAAgB,EAAE,GAAGL;IAClF,MAAM,EAAEM,YAAY,EAAE,GAAGL;IACzB,MAAMM,eAAeJ,wBAAwBI;IAE7C,MAAMC,eAAe,CAACC;QACpBA,EAAEC,cAAc;QAChB,IAAIH,iBAAiBI,WAAW;YAC9B,MAAM,IAAIC,MAAM;QAClB;QACAP,iBAAiBE;QACjBH;QACAE,aAAaK;IACf;IAEA,qBACE,MAACb;QAAOe,MAAMV,2BAA2BQ;;0BACvC,KAACb,OAAOgB,MAAM;0BAAC;;0BAEf,MAACC;gBAAKC,UAAUR;;kCACd,MAACV,OAAOmB,OAAO;wBAACC,QAAQ;wBAACC,IAAI;4BAAEC,OAAO;wBAAQ;;4BAAG;4BACdjB,wBAAwBkB,kBAAkB;4BAAc;;;kCAG3F,MAACvB,OAAOwB,OAAO;;0CACb,KAACvB;gCAAOwB,SAAQ;gCAAYC,MAAK;0CAAS;;0CAG1C,KAACzB;gCAAOwB,SAAQ;gCAAWE,OAAM;gCAAYC,SAAS,IAAMtB;0CAA+B;;;;;;;;AAOrG,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"GridItemContent.d.ts","sourceRoot":"","sources":["../../../src/components/GridLayout/GridItemContent.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAA6D,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAqB,YAAY,EAAE,MAAM,UAAU,CAAC;AAG3D,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,CA8EzE"}
1
+ {"version":3,"file":"GridItemContent.d.ts","sourceRoot":"","sources":["../../../src/components/GridLayout/GridItemContent.tsx"],"names":[],"mappings":"AAgBA,OAAc,EAAE,YAAY,EAAqB,MAAM,OAAO,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAA6D,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAqB,YAAY,EAAE,MAAM,UAAU,CAAC;AAI3D,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,CAmGzE"}