@perses-dev/plugin-system 0.54.0-beta.4 → 0.54.0-beta.6

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 (54) hide show
  1. package/dist/cjs/components/Annotations/AnnotationEditorForm/AnnotationEditorForm.js +283 -0
  2. package/dist/cjs/components/Annotations/AnnotationEditorForm/AnnotationPreview.js +252 -0
  3. package/dist/cjs/components/Annotations/AnnotationEditorForm/index.js +30 -0
  4. package/dist/cjs/components/Annotations/index.js +30 -0
  5. package/dist/cjs/components/index.js +1 -0
  6. package/dist/cjs/context/ValidationProvider.js +7 -1
  7. package/dist/cjs/model/annotations.js +16 -0
  8. package/dist/cjs/model/index.js +1 -0
  9. package/dist/cjs/runtime/annotations.js +148 -0
  10. package/dist/cjs/runtime/index.js +1 -0
  11. package/dist/components/Annotations/AnnotationEditorForm/AnnotationEditorForm.d.ts +16 -0
  12. package/dist/components/Annotations/AnnotationEditorForm/AnnotationEditorForm.d.ts.map +1 -0
  13. package/dist/components/Annotations/AnnotationEditorForm/AnnotationEditorForm.js +270 -0
  14. package/dist/components/Annotations/AnnotationEditorForm/AnnotationEditorForm.js.map +1 -0
  15. package/dist/components/Annotations/AnnotationEditorForm/AnnotationPreview.d.ts +8 -0
  16. package/dist/components/Annotations/AnnotationEditorForm/AnnotationPreview.d.ts.map +1 -0
  17. package/dist/components/Annotations/AnnotationEditorForm/AnnotationPreview.js +198 -0
  18. package/dist/components/Annotations/AnnotationEditorForm/AnnotationPreview.js.map +1 -0
  19. package/dist/components/Annotations/AnnotationEditorForm/index.d.ts +2 -0
  20. package/dist/components/Annotations/AnnotationEditorForm/index.d.ts.map +1 -0
  21. package/dist/components/Annotations/AnnotationEditorForm/index.js +15 -0
  22. package/dist/components/Annotations/AnnotationEditorForm/index.js.map +1 -0
  23. package/dist/components/Annotations/index.d.ts +2 -0
  24. package/dist/components/Annotations/index.d.ts.map +1 -0
  25. package/dist/components/Annotations/index.js +15 -0
  26. package/dist/components/Annotations/index.js.map +1 -0
  27. package/dist/components/index.d.ts +1 -0
  28. package/dist/components/index.d.ts.map +1 -1
  29. package/dist/components/index.js +1 -0
  30. package/dist/components/index.js.map +1 -1
  31. package/dist/context/ValidationProvider.d.ts +3 -1
  32. package/dist/context/ValidationProvider.d.ts.map +1 -1
  33. package/dist/context/ValidationProvider.js +8 -2
  34. package/dist/context/ValidationProvider.js.map +1 -1
  35. package/dist/model/annotations.d.ts +28 -0
  36. package/dist/model/annotations.d.ts.map +1 -0
  37. package/dist/model/annotations.js +17 -0
  38. package/dist/model/annotations.js.map +1 -0
  39. package/dist/model/index.d.ts +1 -0
  40. package/dist/model/index.d.ts.map +1 -1
  41. package/dist/model/index.js +1 -0
  42. package/dist/model/index.js.map +1 -1
  43. package/dist/model/plugins.d.ts +2 -0
  44. package/dist/model/plugins.d.ts.map +1 -1
  45. package/dist/model/plugins.js.map +1 -1
  46. package/dist/runtime/annotations.d.ts +6 -0
  47. package/dist/runtime/annotations.d.ts.map +1 -0
  48. package/dist/runtime/annotations.js +129 -0
  49. package/dist/runtime/annotations.js.map +1 -0
  50. package/dist/runtime/index.d.ts +1 -0
  51. package/dist/runtime/index.d.ts.map +1 -1
  52. package/dist/runtime/index.js +1 -0
  53. package/dist/runtime/index.js.map +1 -1
  54. package/package.json +3 -3
@@ -0,0 +1,148 @@
1
+ // Copyright 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: Object.getOwnPropertyDescriptor(all, name).get
21
+ });
22
+ }
23
+ _export(exports, {
24
+ get ANNOTATION_KEY () {
25
+ return ANNOTATION_KEY;
26
+ },
27
+ get useAnnotationData () {
28
+ return useAnnotationData;
29
+ },
30
+ get useAnnotations () {
31
+ return useAnnotations;
32
+ }
33
+ });
34
+ const _reactquery = require("@tanstack/react-query");
35
+ const _pluginregistry = require("./plugin-registry");
36
+ const _TimeRangeProvider = require("./TimeRangeProvider");
37
+ const _variables = require("./variables");
38
+ const _datasources = require("./datasources");
39
+ const _utils = require("./utils");
40
+ const ANNOTATION_KEY = 'Annotation';
41
+ function useAnnotationContext() {
42
+ const { absoluteTimeRange } = (0, _TimeRangeProvider.useTimeRange)();
43
+ const variableState = (0, _variables.useAllVariableValues)();
44
+ const datasourceStore = (0, _datasources.useDatasourceStore)();
45
+ return {
46
+ variableState,
47
+ datasourceStore,
48
+ absoluteTimeRange
49
+ };
50
+ }
51
+ function getQueryOptions({ plugin, definition, context }) {
52
+ const { variableState, absoluteTimeRange } = context;
53
+ const dependencies = plugin?.dependsOn ? plugin.dependsOn(definition.plugin.spec, context) : {};
54
+ const variableDependencies = dependencies?.variables;
55
+ const filteredVariableState = (0, _utils.filterVariableStateMap)(variableState, variableDependencies);
56
+ const variablesValueKey = (0, _utils.getVariableValuesKey)(filteredVariableState);
57
+ const queryKey = [
58
+ ANNOTATION_KEY,
59
+ definition,
60
+ absoluteTimeRange,
61
+ variablesValueKey
62
+ ];
63
+ let waitToLoad = false;
64
+ if (variableDependencies) {
65
+ waitToLoad = variableDependencies.some((v)=>variableState[v]?.loading);
66
+ }
67
+ const queryEnabled = plugin !== undefined && !waitToLoad;
68
+ return {
69
+ queryKey,
70
+ queryEnabled
71
+ };
72
+ }
73
+ function useAnnotations(definitions) {
74
+ const { getPlugin } = (0, _pluginregistry.usePluginRegistry)();
75
+ const context = useAnnotationContext();
76
+ const pluginLoaderResponse = (0, _pluginregistry.usePlugins)('Annotation', definitions.map((d)=>({
77
+ kind: d.plugin.kind
78
+ })));
79
+ // useQueries() handles data fetching from query plugins
80
+ return (0, _reactquery.useQueries)({
81
+ queries: definitions.map((definition, idx)=>{
82
+ const plugin = pluginLoaderResponse[idx]?.data;
83
+ const { queryEnabled, queryKey } = getQueryOptions({
84
+ context,
85
+ definition,
86
+ plugin
87
+ });
88
+ const annotationKind = definition?.plugin?.kind;
89
+ return {
90
+ enabled: queryEnabled,
91
+ queryKey: queryKey,
92
+ refetchOnMount: false,
93
+ refetchOnWindowFocus: false,
94
+ refetchOnReconnect: false,
95
+ staleTime: Infinity,
96
+ queryFn: async ({ signal })=>{
97
+ const plugin = await getPlugin({
98
+ kind: ANNOTATION_KEY,
99
+ name: annotationKind
100
+ });
101
+ const data = await plugin.getAnnotationData(definition.plugin.spec, context, signal);
102
+ return data;
103
+ }
104
+ };
105
+ })
106
+ });
107
+ }
108
+ function useAnnotationData(spec) {
109
+ const { data: annotationPlugin } = (0, _pluginregistry.usePlugin)('Annotation', spec.plugin.kind);
110
+ const datasourceStore = (0, _datasources.useDatasourceStore)();
111
+ const allVariables = (0, _variables.useAllVariableValues)();
112
+ const { absoluteTimeRange: timeRange } = (0, _TimeRangeProvider.useTimeRange)();
113
+ const variablePluginCtx = {
114
+ absoluteTimeRange: timeRange,
115
+ datasourceStore,
116
+ variableState: allVariables
117
+ };
118
+ let dependsOnVariables = Object.keys(allVariables); // Default to all variables
119
+ if (annotationPlugin?.dependsOn) {
120
+ const dependencies = annotationPlugin.dependsOn(spec.plugin.spec, variablePluginCtx);
121
+ dependsOnVariables = dependencies.variables ? dependencies.variables : dependsOnVariables;
122
+ }
123
+ const variables = (0, _variables.useAllVariableValues)(dependsOnVariables);
124
+ let waitToLoad = false;
125
+ if (dependsOnVariables) {
126
+ waitToLoad = dependsOnVariables.some((v)=>variables[v]?.loading);
127
+ }
128
+ const variablesValueKey = (0, _utils.getVariableValuesKey)(variables);
129
+ return (0, _reactquery.useQuery)({
130
+ queryKey: [
131
+ 'annotation',
132
+ spec,
133
+ timeRange,
134
+ variablesValueKey
135
+ ],
136
+ queryFn: async ({ signal })=>{
137
+ const resp = await annotationPlugin?.getAnnotationData(spec.plugin.spec, {
138
+ ...variablePluginCtx,
139
+ variableState: variables
140
+ }, signal);
141
+ if (!resp?.length) {
142
+ return [];
143
+ }
144
+ return resp;
145
+ },
146
+ enabled: !!annotationPlugin || waitToLoad
147
+ });
148
+ }
@@ -14,6 +14,7 @@
14
14
  Object.defineProperty(exports, "__esModule", {
15
15
  value: true
16
16
  });
17
+ _export_star(require("./annotations"), exports);
17
18
  _export_star(require("./builtin-variables"), exports);
18
19
  _export_star(require("./datasources"), exports);
19
20
  _export_star(require("./plugin-registry"), exports);
@@ -0,0 +1,16 @@
1
+ import { DispatchWithoutAction, ReactElement } from 'react';
2
+ import { AnnotationSpec } from '@perses-dev/spec';
3
+ import { Action } from '@perses-dev/components';
4
+ interface AnnotationEditorFormProps {
5
+ initialAnnotationSpec: AnnotationSpec;
6
+ action: Action;
7
+ isDraft: boolean;
8
+ isReadonly?: boolean;
9
+ onActionChange?: (action: Action) => void;
10
+ onSave: (def: AnnotationSpec) => void;
11
+ onClose: () => void;
12
+ onDelete?: DispatchWithoutAction;
13
+ }
14
+ export declare function AnnotationEditorForm({ initialAnnotationSpec, action, isDraft, isReadonly, onActionChange, onSave, onClose, onDelete, }: AnnotationEditorFormProps): ReactElement;
15
+ export {};
16
+ //# sourceMappingURL=AnnotationEditorForm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnnotationEditorForm.d.ts","sourceRoot":"","sources":["../../../../src/components/Annotations/AnnotationEditorForm/AnnotationEditorForm.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAyB,MAAM,OAAO,CAAC;AAEnF,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAML,MAAM,EAGP,MAAM,wBAAwB,CAAC;AAwDhC,UAAU,yBAAyB;IACjC,qBAAqB,EAAE,cAAc,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACtC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC;AAED,wBAAgB,oBAAoB,CAAC,EACnC,qBAAqB,EACrB,MAAM,EACN,OAAO,EACP,UAAU,EACV,cAAc,EACd,MAAM,EACN,OAAO,EACP,QAAQ,GACT,EAAE,yBAAyB,GAAG,YAAY,CAuK1C"}
@@ -0,0 +1,270 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ // Copyright The Perses Authors
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ import { useCallback, useState } from 'react';
15
+ import { Box, Typography, TextField, Grid, Divider, Stack, IconButton } from '@mui/material';
16
+ import { DiscardChangesConfirmationDialog, ErrorAlert, ErrorBoundary, FormActions, OptionsColorPicker, getSubmitText, getTitleAction } from '@perses-dev/components';
17
+ import { Controller, FormProvider, useForm, useWatch } from 'react-hook-form';
18
+ import { zodResolver } from '@hookform/resolvers/zod';
19
+ import { useQueryClient } from '@tanstack/react-query';
20
+ import InvertColorsIcon from 'mdi-material-ui/InvertColors';
21
+ import { PluginEditor } from '../../PluginEditor';
22
+ import { useValidationSchemas } from '../../../context';
23
+ import { AnnotationPreview } from './AnnotationPreview';
24
+ const DEFAULT_ANNOTATION_COLOR = '#FF6B6B';
25
+ function FallbackPreview() {
26
+ return /*#__PURE__*/ _jsx("div", {
27
+ children: "Error previewing annotations"
28
+ });
29
+ }
30
+ function AnnotationPluginControl({ action, control, onRunQuery }) {
31
+ const plugin = useWatch({
32
+ control,
33
+ name: 'plugin'
34
+ });
35
+ const kind = plugin?.kind;
36
+ const pluginSpec = plugin?.spec;
37
+ return /*#__PURE__*/ _jsx(Controller, {
38
+ control: control,
39
+ name: "plugin",
40
+ render: ({ field })=>{
41
+ return /*#__PURE__*/ _jsx(PluginEditor, {
42
+ withRunQueryButton: true,
43
+ width: "100%",
44
+ pluginTypes: [
45
+ 'Annotation'
46
+ ],
47
+ pluginKindLabel: "Source",
48
+ value: {
49
+ selection: {
50
+ type: 'Annotation',
51
+ kind: kind ?? 'StaticListAnnotation'
52
+ },
53
+ spec: pluginSpec ?? {}
54
+ },
55
+ isReadonly: action === 'read',
56
+ onChange: (v)=>{
57
+ field.onChange({
58
+ kind: v.selection.kind,
59
+ spec: v.spec
60
+ });
61
+ },
62
+ onRunQuery: onRunQuery
63
+ });
64
+ }
65
+ });
66
+ }
67
+ export function AnnotationEditorForm({ initialAnnotationSpec, action, isDraft, isReadonly, onActionChange, onSave, onClose, onDelete }) {
68
+ const queryClient = useQueryClient();
69
+ const [isDiscardDialogOpened, setDiscardDialogOpened] = useState(false);
70
+ const titleAction = getTitleAction(action, isDraft);
71
+ const submitText = getSubmitText(action, isDraft);
72
+ const { annotationEditorSchema } = useValidationSchemas();
73
+ const form = useForm({
74
+ resolver: zodResolver(annotationEditorSchema),
75
+ mode: 'onBlur',
76
+ defaultValues: initialAnnotationSpec
77
+ });
78
+ /* We use `previewDefinition` to explicitly update the spec
79
+ * that will be used for preview when running query. The reason why we do this is to avoid
80
+ * having to re-fetch the values when the user is still editing the spec.
81
+ * Using structuredClone to not have reference issues with nested objects.
82
+ */ const [previewSpec, setPreviewSpec] = useState(structuredClone(form.getValues()));
83
+ const handleRunQuery = useCallback(async ()=>{
84
+ const values = form.getValues();
85
+ if (JSON.stringify(previewSpec) === JSON.stringify(values)) {
86
+ await queryClient.invalidateQueries({
87
+ queryKey: [
88
+ 'annotation',
89
+ previewSpec
90
+ ]
91
+ });
92
+ } else {
93
+ setPreviewSpec(structuredClone(values));
94
+ }
95
+ }, [
96
+ form,
97
+ previewSpec,
98
+ queryClient
99
+ ]);
100
+ const processForm = (data)=>{
101
+ // reset display attributes to undefined when empty, because we don't want to save empty strings
102
+ onSave(data);
103
+ };
104
+ function handleCancel() {
105
+ if (JSON.stringify(initialAnnotationSpec) !== JSON.stringify(form.getValues())) {
106
+ setDiscardDialogOpened(true);
107
+ } else {
108
+ onClose();
109
+ }
110
+ }
111
+ return /*#__PURE__*/ _jsxs(FormProvider, {
112
+ ...form,
113
+ children: [
114
+ /*#__PURE__*/ _jsxs(Box, {
115
+ sx: {
116
+ display: 'flex',
117
+ alignItems: 'center',
118
+ padding: (theme)=>theme.spacing(1, 2),
119
+ borderBottom: (theme)=>`1px solid ${theme.palette.divider}`
120
+ },
121
+ children: [
122
+ /*#__PURE__*/ _jsxs(Typography, {
123
+ variant: "h2",
124
+ children: [
125
+ titleAction,
126
+ " Annotation"
127
+ ]
128
+ }),
129
+ /*#__PURE__*/ _jsx(FormActions, {
130
+ action: action,
131
+ submitText: submitText,
132
+ isReadonly: isReadonly,
133
+ isValid: form.formState.isValid,
134
+ onActionChange: onActionChange,
135
+ onSubmit: form.handleSubmit(processForm),
136
+ onDelete: onDelete,
137
+ onCancel: handleCancel
138
+ })
139
+ ]
140
+ }),
141
+ /*#__PURE__*/ _jsxs(Box, {
142
+ padding: 2,
143
+ sx: {
144
+ overflowY: 'scroll'
145
+ },
146
+ children: [
147
+ /*#__PURE__*/ _jsxs(Grid, {
148
+ container: true,
149
+ spacing: 2,
150
+ mb: 2,
151
+ children: [
152
+ /*#__PURE__*/ _jsx(Grid, {
153
+ item: true,
154
+ xs: 12,
155
+ children: /*#__PURE__*/ _jsx(Controller, {
156
+ control: form.control,
157
+ name: "display.name",
158
+ render: ({ field, fieldState })=>/*#__PURE__*/ _jsx(TextField, {
159
+ ...field,
160
+ required: true,
161
+ fullWidth: true,
162
+ label: "Name",
163
+ InputLabelProps: {
164
+ shrink: action === 'read' ? true : undefined
165
+ },
166
+ InputProps: {
167
+ disabled: action === 'update' && !isDraft,
168
+ readOnly: action === 'read'
169
+ },
170
+ error: !!fieldState.error,
171
+ helperText: fieldState.error?.message,
172
+ value: field.value ?? '',
173
+ onChange: (event)=>{
174
+ field.onChange(event);
175
+ }
176
+ })
177
+ })
178
+ }),
179
+ /*#__PURE__*/ _jsx(Grid, {
180
+ item: true,
181
+ xs: 12,
182
+ children: /*#__PURE__*/ _jsxs(Stack, {
183
+ direction: "row",
184
+ gap: 1,
185
+ children: [
186
+ /*#__PURE__*/ _jsx(Controller, {
187
+ control: form.control,
188
+ name: "display.color",
189
+ render: ({ field })=>{
190
+ const isEnabled = field.value !== undefined;
191
+ return /*#__PURE__*/ _jsx(_Fragment, {
192
+ children: isEnabled ? /*#__PURE__*/ _jsx(OptionsColorPicker, {
193
+ size: "medium",
194
+ label: "annotation",
195
+ color: field.value ?? DEFAULT_ANNOTATION_COLOR,
196
+ onColorChange: (color)=>field.onChange(color),
197
+ onClear: ()=>field.onChange(undefined)
198
+ }) : /*#__PURE__*/ _jsx(IconButton, {
199
+ size: "medium",
200
+ onClick: ()=>field.onChange(DEFAULT_ANNOTATION_COLOR),
201
+ children: /*#__PURE__*/ _jsx(InvertColorsIcon, {})
202
+ })
203
+ });
204
+ }
205
+ }),
206
+ /*#__PURE__*/ _jsx(Controller, {
207
+ control: form.control,
208
+ name: "display.description",
209
+ render: ({ field, fieldState })=>/*#__PURE__*/ _jsx(TextField, {
210
+ ...field,
211
+ fullWidth: true,
212
+ label: "Description",
213
+ InputLabelProps: {
214
+ shrink: action === 'read' ? true : undefined
215
+ },
216
+ InputProps: {
217
+ readOnly: action === 'read'
218
+ },
219
+ error: !!fieldState.error,
220
+ helperText: fieldState.error?.message,
221
+ value: field.value ?? '',
222
+ onChange: (event)=>{
223
+ field.onChange(event);
224
+ }
225
+ })
226
+ })
227
+ ]
228
+ })
229
+ })
230
+ ]
231
+ }),
232
+ /*#__PURE__*/ _jsx(Divider, {}),
233
+ /*#__PURE__*/ _jsx(ErrorBoundary, {
234
+ FallbackComponent: FallbackPreview,
235
+ resetKeys: [
236
+ previewSpec
237
+ ],
238
+ children: /*#__PURE__*/ _jsx(AnnotationPreview, {
239
+ spec: previewSpec,
240
+ sx: {
241
+ marginY: 2
242
+ }
243
+ })
244
+ }),
245
+ /*#__PURE__*/ _jsx(ErrorBoundary, {
246
+ FallbackComponent: ErrorAlert,
247
+ children: /*#__PURE__*/ _jsx(AnnotationPluginControl, {
248
+ action: action,
249
+ control: form.control,
250
+ onRunQuery: handleRunQuery
251
+ })
252
+ })
253
+ ]
254
+ }),
255
+ /*#__PURE__*/ _jsx(DiscardChangesConfirmationDialog, {
256
+ description: "Are you sure you want to discard these changes? Changes cannot be recovered.",
257
+ isOpen: isDiscardDialogOpened,
258
+ onCancel: ()=>{
259
+ setDiscardDialogOpened(false);
260
+ },
261
+ onDiscardChanges: ()=>{
262
+ setDiscardDialogOpened(false);
263
+ onClose();
264
+ }
265
+ })
266
+ ]
267
+ });
268
+ }
269
+
270
+ //# sourceMappingURL=AnnotationEditorForm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/Annotations/AnnotationEditorForm/AnnotationEditorForm.tsx"],"sourcesContent":["// Copyright 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 { DispatchWithoutAction, ReactElement, useCallback, useState } from 'react';\nimport { Box, Typography, TextField, Grid, Divider, Stack, IconButton } from '@mui/material';\nimport { AnnotationSpec } from '@perses-dev/spec';\nimport {\n DiscardChangesConfirmationDialog,\n ErrorAlert,\n ErrorBoundary,\n FormActions,\n OptionsColorPicker,\n Action,\n getSubmitText,\n getTitleAction,\n} from '@perses-dev/components';\nimport { Control, Controller, FormProvider, SubmitHandler, useForm, useWatch } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { useQueryClient } from '@tanstack/react-query';\nimport InvertColorsIcon from 'mdi-material-ui/InvertColors';\nimport { PluginEditor } from '../../PluginEditor';\nimport { useValidationSchemas } from '../../../context';\nimport { AnnotationPreview } from './AnnotationPreview';\n\nconst DEFAULT_ANNOTATION_COLOR = '#FF6B6B';\n\nfunction FallbackPreview(): ReactElement {\n return <div>Error previewing annotations</div>;\n}\n\ninterface KindAnnotationEditorFormProps {\n action: Action;\n control: Control<AnnotationSpec>;\n onRunQuery: () => void;\n}\n\nfunction AnnotationPluginControl({ action, control, onRunQuery }: KindAnnotationEditorFormProps): ReactElement {\n const plugin = useWatch<AnnotationSpec, 'plugin'>({ control, name: 'plugin' });\n const kind = plugin?.kind;\n const pluginSpec = plugin?.spec;\n\n return (\n <Controller\n control={control}\n name=\"plugin\"\n render={({ field }) => {\n return (\n <PluginEditor\n withRunQueryButton\n width=\"100%\"\n pluginTypes={['Annotation']}\n pluginKindLabel=\"Source\"\n value={{\n selection: {\n type: 'Annotation',\n kind: kind ?? 'StaticListAnnotation',\n },\n spec: pluginSpec ?? {},\n }}\n isReadonly={action === 'read'}\n onChange={(v) => {\n field.onChange({ kind: v.selection.kind, spec: v.spec });\n }}\n onRunQuery={onRunQuery}\n />\n );\n }}\n />\n );\n}\n\ninterface AnnotationEditorFormProps {\n initialAnnotationSpec: AnnotationSpec;\n action: Action;\n isDraft: boolean;\n isReadonly?: boolean;\n onActionChange?: (action: Action) => void;\n onSave: (def: AnnotationSpec) => void;\n onClose: () => void;\n onDelete?: DispatchWithoutAction;\n}\n\nexport function AnnotationEditorForm({\n initialAnnotationSpec,\n action,\n isDraft,\n isReadonly,\n onActionChange,\n onSave,\n onClose,\n onDelete,\n}: AnnotationEditorFormProps): ReactElement {\n const queryClient = useQueryClient();\n\n const [isDiscardDialogOpened, setDiscardDialogOpened] = useState<boolean>(false);\n const titleAction = getTitleAction(action, isDraft);\n const submitText = getSubmitText(action, isDraft);\n\n const { annotationEditorSchema } = useValidationSchemas();\n const form = useForm<AnnotationSpec>({\n resolver: zodResolver(annotationEditorSchema),\n mode: 'onBlur',\n defaultValues: initialAnnotationSpec,\n });\n\n /* We use `previewDefinition` to explicitly update the spec\n * that will be used for preview when running query. The reason why we do this is to avoid\n * having to re-fetch the values when the user is still editing the spec.\n * Using structuredClone to not have reference issues with nested objects.\n */\n const [previewSpec, setPreviewSpec] = useState(structuredClone(form.getValues()));\n\n const handleRunQuery = useCallback(async () => {\n const values = form.getValues();\n if (JSON.stringify(previewSpec) === JSON.stringify(values)) {\n await queryClient.invalidateQueries({ queryKey: ['annotation', previewSpec] });\n } else {\n setPreviewSpec(structuredClone(values));\n }\n }, [form, previewSpec, queryClient]);\n\n const processForm: SubmitHandler<AnnotationSpec> = (data: AnnotationSpec) => {\n // reset display attributes to undefined when empty, because we don't want to save empty strings\n onSave(data);\n };\n\n function handleCancel(): void {\n if (JSON.stringify(initialAnnotationSpec) !== JSON.stringify(form.getValues())) {\n setDiscardDialogOpened(true);\n } else {\n onClose();\n }\n }\n\n return (\n <FormProvider {...form}>\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n padding: (theme) => theme.spacing(1, 2),\n borderBottom: (theme) => `1px solid ${theme.palette.divider}`,\n }}\n >\n <Typography variant=\"h2\">{titleAction} Annotation</Typography>\n <FormActions\n action={action}\n submitText={submitText}\n isReadonly={isReadonly}\n isValid={form.formState.isValid}\n onActionChange={onActionChange}\n onSubmit={form.handleSubmit(processForm)}\n onDelete={onDelete}\n onCancel={handleCancel}\n />\n </Box>\n <Box padding={2} sx={{ overflowY: 'scroll' }}>\n <Grid container spacing={2} mb={2}>\n <Grid item xs={12}>\n <Controller\n control={form.control}\n name=\"display.name\"\n render={({ field, fieldState }) => (\n <TextField\n {...field}\n required\n fullWidth\n label=\"Name\"\n InputLabelProps={{ shrink: action === 'read' ? true : undefined }}\n InputProps={{\n disabled: action === 'update' && !isDraft,\n readOnly: action === 'read',\n }}\n error={!!fieldState.error}\n helperText={fieldState.error?.message}\n value={field.value ?? ''}\n onChange={(event) => {\n field.onChange(event);\n }}\n />\n )}\n />\n </Grid>\n <Grid item xs={12}>\n <Stack direction=\"row\" gap={1}>\n <Controller\n control={form.control}\n name=\"display.color\"\n render={({ field }) => {\n const isEnabled = field.value !== undefined;\n return (\n <>\n {isEnabled ? (\n <OptionsColorPicker\n size=\"medium\"\n label=\"annotation\"\n color={field.value ?? DEFAULT_ANNOTATION_COLOR}\n onColorChange={(color) => field.onChange(color)}\n onClear={() => field.onChange(undefined)}\n />\n ) : (\n <IconButton size=\"medium\" onClick={() => field.onChange(DEFAULT_ANNOTATION_COLOR)}>\n <InvertColorsIcon />\n </IconButton>\n )}\n </>\n );\n }}\n />\n\n <Controller\n control={form.control}\n name=\"display.description\"\n render={({ field, fieldState }) => (\n <TextField\n {...field}\n fullWidth\n label=\"Description\"\n InputLabelProps={{ shrink: action === 'read' ? true : undefined }}\n InputProps={{\n readOnly: action === 'read',\n }}\n error={!!fieldState.error}\n helperText={fieldState.error?.message}\n value={field.value ?? ''}\n onChange={(event) => {\n field.onChange(event);\n }}\n />\n )}\n />\n </Stack>\n </Grid>\n </Grid>\n\n <Divider />\n\n <ErrorBoundary FallbackComponent={FallbackPreview} resetKeys={[previewSpec]}>\n <AnnotationPreview spec={previewSpec} sx={{ marginY: 2 }} />\n </ErrorBoundary>\n\n <ErrorBoundary FallbackComponent={ErrorAlert}>\n <AnnotationPluginControl action={action} control={form.control} onRunQuery={handleRunQuery} />\n </ErrorBoundary>\n </Box>\n <DiscardChangesConfirmationDialog\n description=\"Are you sure you want to discard these changes? Changes cannot be recovered.\"\n isOpen={isDiscardDialogOpened}\n onCancel={() => {\n setDiscardDialogOpened(false);\n }}\n onDiscardChanges={() => {\n setDiscardDialogOpened(false);\n onClose();\n }}\n />\n </FormProvider>\n );\n}\n"],"names":["useCallback","useState","Box","Typography","TextField","Grid","Divider","Stack","IconButton","DiscardChangesConfirmationDialog","ErrorAlert","ErrorBoundary","FormActions","OptionsColorPicker","getSubmitText","getTitleAction","Controller","FormProvider","useForm","useWatch","zodResolver","useQueryClient","InvertColorsIcon","PluginEditor","useValidationSchemas","AnnotationPreview","DEFAULT_ANNOTATION_COLOR","FallbackPreview","div","AnnotationPluginControl","action","control","onRunQuery","plugin","name","kind","pluginSpec","spec","render","field","withRunQueryButton","width","pluginTypes","pluginKindLabel","value","selection","type","isReadonly","onChange","v","AnnotationEditorForm","initialAnnotationSpec","isDraft","onActionChange","onSave","onClose","onDelete","queryClient","isDiscardDialogOpened","setDiscardDialogOpened","titleAction","submitText","annotationEditorSchema","form","resolver","mode","defaultValues","previewSpec","setPreviewSpec","structuredClone","getValues","handleRunQuery","values","JSON","stringify","invalidateQueries","queryKey","processForm","data","handleCancel","sx","display","alignItems","padding","theme","spacing","borderBottom","palette","divider","variant","isValid","formState","onSubmit","handleSubmit","onCancel","overflowY","container","mb","item","xs","fieldState","required","fullWidth","label","InputLabelProps","shrink","undefined","InputProps","disabled","readOnly","error","helperText","message","event","direction","gap","isEnabled","size","color","onColorChange","onClear","onClick","FallbackComponent","resetKeys","marginY","description","isOpen","onDiscardChanges"],"mappings":";AAAA,+BAA+B;AAC/B,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AAEjC,SAA8CA,WAAW,EAAEC,QAAQ,QAAQ,QAAQ;AACnF,SAASC,GAAG,EAAEC,UAAU,EAAEC,SAAS,EAAEC,IAAI,EAAEC,OAAO,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAgB;AAE7F,SACEC,gCAAgC,EAChCC,UAAU,EACVC,aAAa,EACbC,WAAW,EACXC,kBAAkB,EAElBC,aAAa,EACbC,cAAc,QACT,yBAAyB;AAChC,SAAkBC,UAAU,EAAEC,YAAY,EAAiBC,OAAO,EAAEC,QAAQ,QAAQ,kBAAkB;AACtG,SAASC,WAAW,QAAQ,0BAA0B;AACtD,SAASC,cAAc,QAAQ,wBAAwB;AACvD,OAAOC,sBAAsB,+BAA+B;AAC5D,SAASC,YAAY,QAAQ,qBAAqB;AAClD,SAASC,oBAAoB,QAAQ,mBAAmB;AACxD,SAASC,iBAAiB,QAAQ,sBAAsB;AAExD,MAAMC,2BAA2B;AAEjC,SAASC;IACP,qBAAO,KAACC;kBAAI;;AACd;AAQA,SAASC,wBAAwB,EAAEC,MAAM,EAAEC,OAAO,EAAEC,UAAU,EAAiC;IAC7F,MAAMC,SAASd,SAAmC;QAAEY;QAASG,MAAM;IAAS;IAC5E,MAAMC,OAAOF,QAAQE;IACrB,MAAMC,aAAaH,QAAQI;IAE3B,qBACE,KAACrB;QACCe,SAASA;QACTG,MAAK;QACLI,QAAQ,CAAC,EAAEC,KAAK,EAAE;YAChB,qBACE,KAAChB;gBACCiB,kBAAkB;gBAClBC,OAAM;gBACNC,aAAa;oBAAC;iBAAa;gBAC3BC,iBAAgB;gBAChBC,OAAO;oBACLC,WAAW;wBACTC,MAAM;wBACNX,MAAMA,QAAQ;oBAChB;oBACAE,MAAMD,cAAc,CAAC;gBACvB;gBACAW,YAAYjB,WAAW;gBACvBkB,UAAU,CAACC;oBACTV,MAAMS,QAAQ,CAAC;wBAAEb,MAAMc,EAAEJ,SAAS,CAACV,IAAI;wBAAEE,MAAMY,EAAEZ,IAAI;oBAAC;gBACxD;gBACAL,YAAYA;;QAGlB;;AAGN;AAaA,OAAO,SAASkB,qBAAqB,EACnCC,qBAAqB,EACrBrB,MAAM,EACNsB,OAAO,EACPL,UAAU,EACVM,cAAc,EACdC,MAAM,EACNC,OAAO,EACPC,QAAQ,EACkB;IAC1B,MAAMC,cAAcpC;IAEpB,MAAM,CAACqC,uBAAuBC,uBAAuB,GAAG1D,SAAkB;IAC1E,MAAM2D,cAAc7C,eAAee,QAAQsB;IAC3C,MAAMS,aAAa/C,cAAcgB,QAAQsB;IAEzC,MAAM,EAAEU,sBAAsB,EAAE,GAAGtC;IACnC,MAAMuC,OAAO7C,QAAwB;QACnC8C,UAAU5C,YAAY0C;QACtBG,MAAM;QACNC,eAAef;IACjB;IAEA;;;;GAIC,GACD,MAAM,CAACgB,aAAaC,eAAe,GAAGnE,SAASoE,gBAAgBN,KAAKO,SAAS;IAE7E,MAAMC,iBAAiBvE,YAAY;QACjC,MAAMwE,SAAST,KAAKO,SAAS;QAC7B,IAAIG,KAAKC,SAAS,CAACP,iBAAiBM,KAAKC,SAAS,CAACF,SAAS;YAC1D,MAAMf,YAAYkB,iBAAiB,CAAC;gBAAEC,UAAU;oBAAC;oBAAcT;iBAAY;YAAC;QAC9E,OAAO;YACLC,eAAeC,gBAAgBG;QACjC;IACF,GAAG;QAACT;QAAMI;QAAaV;KAAY;IAEnC,MAAMoB,cAA6C,CAACC;QAClD,gGAAgG;QAChGxB,OAAOwB;IACT;IAEA,SAASC;QACP,IAAIN,KAAKC,SAAS,CAACvB,2BAA2BsB,KAAKC,SAAS,CAACX,KAAKO,SAAS,KAAK;YAC9EX,uBAAuB;QACzB,OAAO;YACLJ;QACF;IACF;IAEA,qBACE,MAACtC;QAAc,GAAG8C,IAAI;;0BACpB,MAAC7D;gBACC8E,IAAI;oBACFC,SAAS;oBACTC,YAAY;oBACZC,SAAS,CAACC,QAAUA,MAAMC,OAAO,CAAC,GAAG;oBACrCC,cAAc,CAACF,QAAU,CAAC,UAAU,EAAEA,MAAMG,OAAO,CAACC,OAAO,EAAE;gBAC/D;;kCAEA,MAACrF;wBAAWsF,SAAQ;;4BAAM7B;4BAAY;;;kCACtC,KAAChD;wBACCkB,QAAQA;wBACR+B,YAAYA;wBACZd,YAAYA;wBACZ2C,SAAS3B,KAAK4B,SAAS,CAACD,OAAO;wBAC/BrC,gBAAgBA;wBAChBuC,UAAU7B,KAAK8B,YAAY,CAAChB;wBAC5BrB,UAAUA;wBACVsC,UAAUf;;;;0BAGd,MAAC7E;gBAAIiF,SAAS;gBAAGH,IAAI;oBAAEe,WAAW;gBAAS;;kCACzC,MAAC1F;wBAAK2F,SAAS;wBAACX,SAAS;wBAAGY,IAAI;;0CAC9B,KAAC5F;gCAAK6F,IAAI;gCAACC,IAAI;0CACb,cAAA,KAACnF;oCACCe,SAASgC,KAAKhC,OAAO;oCACrBG,MAAK;oCACLI,QAAQ,CAAC,EAAEC,KAAK,EAAE6D,UAAU,EAAE,iBAC5B,KAAChG;4CACE,GAAGmC,KAAK;4CACT8D,QAAQ;4CACRC,SAAS;4CACTC,OAAM;4CACNC,iBAAiB;gDAAEC,QAAQ3E,WAAW,SAAS,OAAO4E;4CAAU;4CAChEC,YAAY;gDACVC,UAAU9E,WAAW,YAAY,CAACsB;gDAClCyD,UAAU/E,WAAW;4CACvB;4CACAgF,OAAO,CAAC,CAACV,WAAWU,KAAK;4CACzBC,YAAYX,WAAWU,KAAK,EAAEE;4CAC9BpE,OAAOL,MAAMK,KAAK,IAAI;4CACtBI,UAAU,CAACiE;gDACT1E,MAAMS,QAAQ,CAACiE;4CACjB;;;;0CAKR,KAAC5G;gCAAK6F,IAAI;gCAACC,IAAI;0CACb,cAAA,MAAC5F;oCAAM2G,WAAU;oCAAMC,KAAK;;sDAC1B,KAACnG;4CACCe,SAASgC,KAAKhC,OAAO;4CACrBG,MAAK;4CACLI,QAAQ,CAAC,EAAEC,KAAK,EAAE;gDAChB,MAAM6E,YAAY7E,MAAMK,KAAK,KAAK8D;gDAClC,qBACE;8DACGU,0BACC,KAACvG;wDACCwG,MAAK;wDACLd,OAAM;wDACNe,OAAO/E,MAAMK,KAAK,IAAIlB;wDACtB6F,eAAe,CAACD,QAAU/E,MAAMS,QAAQ,CAACsE;wDACzCE,SAAS,IAAMjF,MAAMS,QAAQ,CAAC0D;uEAGhC,KAAClG;wDAAW6G,MAAK;wDAASI,SAAS,IAAMlF,MAAMS,QAAQ,CAACtB;kEACtD,cAAA,KAACJ;;;4CAKX;;sDAGF,KAACN;4CACCe,SAASgC,KAAKhC,OAAO;4CACrBG,MAAK;4CACLI,QAAQ,CAAC,EAAEC,KAAK,EAAE6D,UAAU,EAAE,iBAC5B,KAAChG;oDACE,GAAGmC,KAAK;oDACT+D,SAAS;oDACTC,OAAM;oDACNC,iBAAiB;wDAAEC,QAAQ3E,WAAW,SAAS,OAAO4E;oDAAU;oDAChEC,YAAY;wDACVE,UAAU/E,WAAW;oDACvB;oDACAgF,OAAO,CAAC,CAACV,WAAWU,KAAK;oDACzBC,YAAYX,WAAWU,KAAK,EAAEE;oDAC9BpE,OAAOL,MAAMK,KAAK,IAAI;oDACtBI,UAAU,CAACiE;wDACT1E,MAAMS,QAAQ,CAACiE;oDACjB;;;;;;;;kCAQZ,KAAC3G;kCAED,KAACK;wBAAc+G,mBAAmB/F;wBAAiBgG,WAAW;4BAACxD;yBAAY;kCACzE,cAAA,KAAC1C;4BAAkBY,MAAM8B;4BAAaa,IAAI;gCAAE4C,SAAS;4BAAE;;;kCAGzD,KAACjH;wBAAc+G,mBAAmBhH;kCAChC,cAAA,KAACmB;4BAAwBC,QAAQA;4BAAQC,SAASgC,KAAKhC,OAAO;4BAAEC,YAAYuC;;;;;0BAGhF,KAAC9D;gBACCoH,aAAY;gBACZC,QAAQpE;gBACRoC,UAAU;oBACRnC,uBAAuB;gBACzB;gBACAoE,kBAAkB;oBAChBpE,uBAAuB;oBACvBJ;gBACF;;;;AAIR"}
@@ -0,0 +1,8 @@
1
+ import { ReactNode } from 'react';
2
+ import { AnnotationSpec } from '@perses-dev/spec';
3
+ import { CardProps } from '@mui/material';
4
+ export interface AnnotationPreviewProps extends CardProps {
5
+ spec: AnnotationSpec;
6
+ }
7
+ export declare function AnnotationPreview({ spec, ...props }: AnnotationPreviewProps): ReactNode;
8
+ //# sourceMappingURL=AnnotationPreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnnotationPreview.d.ts","sourceRoot":"","sources":["../../../../src/components/Annotations/AnnotationEditorForm/AnnotationPreview.tsx"],"names":[],"mappings":"AAaA,OAAc,EAAE,SAAS,EAAqB,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAkB,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAIL,SAAS,EAOV,MAAM,eAAe,CAAC;AAmEvB,MAAM,WAAW,sBAAuB,SAAQ,SAAS;IACvD,IAAI,EAAE,cAAc,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,sBAAsB,GAAG,SAAS,CA0DvF"}