@backstage/plugin-scaffolder-react 1.8.4 → 1.8.5-next.0

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 (94) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/alpha/package.json +1 -1
  3. package/dist/alpha.esm.js +19 -1472
  4. package/dist/alpha.esm.js.map +1 -1
  5. package/dist/api/ref.esm.js +12 -0
  6. package/dist/api/ref.esm.js.map +1 -0
  7. package/dist/extensions/index.esm.js +25 -0
  8. package/dist/extensions/index.esm.js.map +1 -0
  9. package/dist/extensions/keys.esm.js +5 -0
  10. package/dist/extensions/keys.esm.js.map +1 -0
  11. package/dist/hooks/useCustomFieldExtensions.esm.js +16 -0
  12. package/dist/hooks/useCustomFieldExtensions.esm.js.map +1 -0
  13. package/dist/hooks/useCustomLayouts.esm.js +16 -0
  14. package/dist/hooks/useCustomLayouts.esm.js.map +1 -0
  15. package/dist/hooks/useEventStream.esm.js +167 -0
  16. package/dist/hooks/useEventStream.esm.js.map +1 -0
  17. package/dist/index.esm.js +7 -228
  18. package/dist/index.esm.js.map +1 -1
  19. package/dist/layouts/createScaffolderLayout.esm.js +17 -0
  20. package/dist/layouts/createScaffolderLayout.esm.js.map +1 -0
  21. package/dist/layouts/keys.esm.js +5 -0
  22. package/dist/layouts/keys.esm.js.map +1 -0
  23. package/dist/next/components/Form/DescriptionFieldTemplate.esm.js +37 -0
  24. package/dist/next/components/Form/DescriptionFieldTemplate.esm.js.map +1 -0
  25. package/dist/next/components/Form/FieldTemplate.esm.js +65 -0
  26. package/dist/next/components/Form/FieldTemplate.esm.js.map +1 -0
  27. package/dist/next/components/Form/Form.esm.js +44 -0
  28. package/dist/next/components/Form/Form.esm.js.map +1 -0
  29. package/dist/next/components/ReviewState/ReviewState.esm.js +43 -0
  30. package/dist/next/components/ReviewState/ReviewState.esm.js.map +1 -0
  31. package/dist/next/components/ScaffolderField/ScaffolderField.esm.js +52 -0
  32. package/dist/next/components/ScaffolderField/ScaffolderField.esm.js.map +1 -0
  33. package/dist/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.esm.js +63 -0
  34. package/dist/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.esm.js.map +1 -0
  35. package/dist/next/components/Stepper/ErrorListTemplate/errorListTemplate.esm.js +32 -0
  36. package/dist/next/components/Stepper/ErrorListTemplate/errorListTemplate.esm.js.map +1 -0
  37. package/dist/next/components/Stepper/FieldOverrides/DescriptionField.esm.js +7 -0
  38. package/dist/next/components/Stepper/FieldOverrides/DescriptionField.esm.js.map +1 -0
  39. package/dist/next/components/Stepper/FieldOverrides/index.esm.js +2 -0
  40. package/dist/next/components/Stepper/FieldOverrides/index.esm.js.map +1 -0
  41. package/dist/next/components/Stepper/Stepper.esm.js +203 -0
  42. package/dist/next/components/Stepper/Stepper.esm.js.map +1 -0
  43. package/dist/next/components/Stepper/createAsyncValidators.esm.js +80 -0
  44. package/dist/next/components/Stepper/createAsyncValidators.esm.js.map +1 -0
  45. package/dist/next/components/Stepper/utils.esm.js +27 -0
  46. package/dist/next/components/Stepper/utils.esm.js.map +1 -0
  47. package/dist/next/components/TaskLogStream/TaskLogStream.esm.js +23 -0
  48. package/dist/next/components/TaskLogStream/TaskLogStream.esm.js.map +1 -0
  49. package/dist/next/components/TaskSteps/StepIcon.esm.js +52 -0
  50. package/dist/next/components/TaskSteps/StepIcon.esm.js.map +1 -0
  51. package/dist/next/components/TaskSteps/StepTime.esm.js +36 -0
  52. package/dist/next/components/TaskSteps/StepTime.esm.js.map +1 -0
  53. package/dist/next/components/TaskSteps/TaskBorder.esm.js +29 -0
  54. package/dist/next/components/TaskSteps/TaskBorder.esm.js.map +1 -0
  55. package/dist/next/components/TaskSteps/TaskSteps.esm.js +54 -0
  56. package/dist/next/components/TaskSteps/TaskSteps.esm.js.map +1 -0
  57. package/dist/next/components/TemplateCard/CardHeader.esm.js +41 -0
  58. package/dist/next/components/TemplateCard/CardHeader.esm.js.map +1 -0
  59. package/dist/next/components/TemplateCard/CardLink.esm.js +17 -0
  60. package/dist/next/components/TemplateCard/CardLink.esm.js.map +1 -0
  61. package/dist/next/components/TemplateCard/TemplateCard.esm.js +114 -0
  62. package/dist/next/components/TemplateCard/TemplateCard.esm.js.map +1 -0
  63. package/dist/next/components/TemplateCategoryPicker/TemplateCategoryPicker.esm.js +80 -0
  64. package/dist/next/components/TemplateCategoryPicker/TemplateCategoryPicker.esm.js.map +1 -0
  65. package/dist/next/components/TemplateGroup/TemplateGroup.esm.js +30 -0
  66. package/dist/next/components/TemplateGroup/TemplateGroup.esm.js.map +1 -0
  67. package/dist/next/components/TemplateGroups/TemplateGroups.esm.js +52 -0
  68. package/dist/next/components/TemplateGroups/TemplateGroups.esm.js.map +1 -0
  69. package/dist/next/components/TemplateOutputs/DefaultTemplateOutputs.esm.js +51 -0
  70. package/dist/next/components/TemplateOutputs/DefaultTemplateOutputs.esm.js.map +1 -0
  71. package/dist/next/components/TemplateOutputs/LinkOutputs.esm.js +40 -0
  72. package/dist/next/components/TemplateOutputs/LinkOutputs.esm.js.map +1 -0
  73. package/dist/next/components/TemplateOutputs/TextOutputs.esm.js +39 -0
  74. package/dist/next/components/TemplateOutputs/TextOutputs.esm.js.map +1 -0
  75. package/dist/next/components/Workflow/Workflow.esm.js +83 -0
  76. package/dist/next/components/Workflow/Workflow.esm.js.map +1 -0
  77. package/dist/next/hooks/useFilteredSchemaProperties.esm.js +44 -0
  78. package/dist/next/hooks/useFilteredSchemaProperties.esm.js.map +1 -0
  79. package/dist/next/hooks/useFormDataFromQuery.esm.js +21 -0
  80. package/dist/next/hooks/useFormDataFromQuery.esm.js.map +1 -0
  81. package/dist/next/hooks/useTemplateParameterSchema.esm.js +19 -0
  82. package/dist/next/hooks/useTemplateParameterSchema.esm.js.map +1 -0
  83. package/dist/next/hooks/useTemplateSchema.esm.js +46 -0
  84. package/dist/next/hooks/useTemplateSchema.esm.js.map +1 -0
  85. package/dist/next/hooks/useTemplateTimeSaved.esm.js +25 -0
  86. package/dist/next/hooks/useTemplateTimeSaved.esm.js.map +1 -0
  87. package/dist/next/hooks/useTransformSchemaToProps.esm.js +24 -0
  88. package/dist/next/hooks/useTransformSchemaToProps.esm.js.map +1 -0
  89. package/dist/next/lib/schema.esm.js +108 -0
  90. package/dist/next/lib/schema.esm.js.map +1 -0
  91. package/dist/{esm/ref-NRtFlQHB.esm.js → secrets/SecretsContext.esm.js} +3 -11
  92. package/dist/secrets/SecretsContext.esm.js.map +1 -0
  93. package/package.json +15 -15
  94. package/dist/esm/ref-NRtFlQHB.esm.js.map +0 -1
package/dist/alpha.esm.js CHANGED
@@ -1,1473 +1,20 @@
1
- import { useApi, featureFlagsApiRef, useAnalytics, useApiHolder, useApp, errorApiRef, useRouteRef, alertApiRef } from '@backstage/core-plugin-api';
2
- import MuiStepper from '@material-ui/core/Stepper';
3
- import MuiStep from '@material-ui/core/Step';
4
- import MuiStepLabel from '@material-ui/core/StepLabel';
5
- import Button from '@material-ui/core/Button';
6
- import LinearProgress from '@material-ui/core/LinearProgress';
7
- import { makeStyles, createStyles, useTheme } from '@material-ui/core/styles';
8
- import React, { useState, useMemo, useCallback, useEffect } from 'react';
9
- import { Draft07 } from 'json-schema-library';
10
- import { parse, stringify } from 'flatted';
11
- import { StructuredMetadataTable, MarkdownContent, ItemCardHeader, Link, UserIcon, Content, ItemCardGrid, ContentHeader, Progress, InfoCard, LogViewer } from '@backstage/core-components';
12
- import validator from '@rjsf/validator-ajv8';
13
- import qs from 'qs';
14
- import useAsync from 'react-use/esm/useAsync';
15
- import { s as scaffolderApiRef, S as SecretsContextProvider } from './esm/ref-NRtFlQHB.esm.js';
16
- import cloneDeep from 'lodash/cloneDeep';
17
- import { withTheme } from '@rjsf/core';
18
- import { getUiOptions, getTemplate } from '@rjsf/utils';
19
- import FormControl from '@material-ui/core/FormControl';
20
- import Typography from '@material-ui/core/Typography';
21
- import { Theme } from '@rjsf/material-ui';
22
- import List from '@material-ui/core/List';
23
- import ListItem from '@material-ui/core/ListItem';
24
- import ListItemIcon from '@material-ui/core/ListItemIcon';
25
- import ListItemText from '@material-ui/core/ListItemText';
26
- import Paper from '@material-ui/core/Paper';
27
- import ErrorIcon from '@material-ui/icons/Error';
28
- import { RELATION_OWNED_BY, stringifyEntityRef, parseEntityRef } from '@backstage/catalog-model';
29
- import { FavoriteEntity, getEntityRelations, EntityRefLinks, useEntityList, catalogApiRef, entityRouteRef, useEntityTypeFilter } from '@backstage/plugin-catalog-react';
30
- import Box from '@material-ui/core/Box';
31
- import Card from '@material-ui/core/Card';
32
- import CardActions from '@material-ui/core/CardActions';
33
- import CardContent from '@material-ui/core/CardContent';
34
- import Chip from '@material-ui/core/Chip';
35
- import Divider from '@material-ui/core/Divider';
36
- import Grid from '@material-ui/core/Grid';
37
- import LanguageIcon from '@material-ui/icons/Language';
38
- import { isTemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
39
- import { Duration, DateTime, Interval } from 'luxon';
40
- import useAsync$1 from 'react-use/lib/useAsync';
41
- import LinkIcon from '@material-ui/icons/Link';
42
- import DescriptionIcon from '@material-ui/icons/Description';
43
- import MuiStepButton from '@material-ui/core/StepButton';
44
- import CircularProgress from '@material-ui/core/CircularProgress';
45
- import RemoveCircleOutline from '@material-ui/icons/RemoveCircleOutline';
46
- import PanoramaFishEyeIcon from '@material-ui/icons/PanoramaFishEye';
47
- import classNames from 'classnames';
48
- import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
49
- import ErrorOutline from '@material-ui/icons/ErrorOutline';
50
- import useInterval from 'react-use/esm/useInterval';
51
- import humanizeDuration from 'humanize-duration';
52
- import { useMountEffect } from '@react-hookz/web';
53
- import capitalize from 'lodash/capitalize';
54
- import Checkbox from '@material-ui/core/Checkbox';
55
- import FormControlLabel from '@material-ui/core/FormControlLabel';
56
- import TextField from '@material-ui/core/TextField';
57
- import CheckBoxIcon from '@material-ui/icons/CheckBox';
58
- import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
59
- import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
60
- import Autocomplete from '@material-ui/lab/Autocomplete';
61
- import IconButton from '@material-ui/core/IconButton';
62
- import MenuItem from '@material-ui/core/MenuItem';
63
- import MenuList from '@material-ui/core/MenuList';
64
- import Popover from '@material-ui/core/Popover';
65
- import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
66
- import Edit from '@material-ui/icons/Edit';
67
- import List$1 from '@material-ui/icons/List';
68
- import MoreVert from '@material-ui/icons/MoreVert';
69
- import '@backstage/version-bridge';
70
-
71
- function isObject$1(value) {
72
- return typeof value === "object" && value !== null && !Array.isArray(value);
73
- }
74
- function extractUiSchema(schema, uiSchema) {
75
- if (!isObject$1(schema)) {
76
- return;
77
- }
78
- const {
79
- properties,
80
- items,
81
- anyOf,
82
- oneOf,
83
- allOf,
84
- dependencies,
85
- then,
86
- else: _else
87
- } = schema;
88
- for (const propName in schema) {
89
- if (!schema.hasOwnProperty(propName)) {
90
- continue;
91
- }
92
- if (propName.startsWith("ui:")) {
93
- uiSchema[propName] = schema[propName];
94
- delete schema[propName];
95
- }
96
- }
97
- if (isObject$1(properties)) {
98
- for (const propName in properties) {
99
- if (!properties.hasOwnProperty(propName)) {
100
- continue;
101
- }
102
- const schemaNode = properties[propName];
103
- if (!isObject$1(schemaNode)) {
104
- continue;
105
- }
106
- if (!isObject$1(uiSchema[propName])) {
107
- const innerUiSchema = {};
108
- uiSchema[propName] = innerUiSchema;
109
- }
110
- extractUiSchema(schemaNode, uiSchema[propName]);
111
- }
112
- }
113
- if (isObject$1(items)) {
114
- const innerUiSchema = {};
115
- uiSchema.items = innerUiSchema;
116
- extractUiSchema(items, innerUiSchema);
117
- }
118
- if (Array.isArray(anyOf)) {
119
- for (const schemaNode of anyOf) {
120
- if (!isObject$1(schemaNode)) {
121
- continue;
122
- }
123
- extractUiSchema(schemaNode, uiSchema);
124
- }
125
- }
126
- if (Array.isArray(oneOf)) {
127
- for (const schemaNode of oneOf) {
128
- if (!isObject$1(schemaNode)) {
129
- continue;
130
- }
131
- extractUiSchema(schemaNode, uiSchema);
132
- }
133
- }
134
- if (Array.isArray(allOf)) {
135
- for (const schemaNode of allOf) {
136
- if (!isObject$1(schemaNode)) {
137
- continue;
138
- }
139
- extractUiSchema(schemaNode, uiSchema);
140
- }
141
- }
142
- if (isObject$1(dependencies)) {
143
- for (const depName of Object.keys(dependencies)) {
144
- const schemaNode = dependencies[depName];
145
- if (!isObject$1(schemaNode)) {
146
- continue;
147
- }
148
- extractUiSchema(schemaNode, uiSchema);
149
- }
150
- }
151
- if (isObject$1(then)) {
152
- extractUiSchema(then, uiSchema);
153
- }
154
- if (isObject$1(_else)) {
155
- extractUiSchema(_else, uiSchema);
156
- }
157
- }
158
- const extractSchemaFromStep = (inputStep) => {
159
- const uiSchema = {};
160
- const returnSchema = parse(stringify(inputStep));
161
- extractUiSchema(returnSchema, uiSchema);
162
- return { uiSchema, schema: returnSchema };
163
- };
164
- const createFieldValidation = () => {
165
- const fieldValidation = {
166
- __errors: [],
167
- addError: (message) => {
168
- var _a;
169
- (_a = fieldValidation.__errors) == null ? void 0 : _a.push(message);
170
- }
171
- };
172
- return fieldValidation;
173
- };
174
-
175
- function isFieldValidation(error) {
176
- return !!error && "__errors" in error;
177
- }
178
- function hasErrors(errors) {
179
- var _a;
180
- if (!errors) {
181
- return false;
182
- }
183
- for (const error of Object.values(errors)) {
184
- if (isFieldValidation(error)) {
185
- if (((_a = error.__errors) != null ? _a : []).length > 0) {
186
- return true;
187
- }
188
- continue;
189
- }
190
- if (hasErrors(error)) {
191
- return true;
192
- }
193
- }
194
- return false;
195
- }
196
- function isObject(value) {
197
- return typeof value === "object" && value !== null && !Array.isArray(value);
198
- }
199
-
200
- const isJsonError = (value) => "type" in value && value.type === "error";
201
- const createAsyncValidators = (rootSchema, validators, context) => {
202
- async function validate(formData, pathPrefix = "#", current = formData) {
203
- var _a, _b;
204
- const parsedSchema = new Draft07(rootSchema);
205
- const formValidation = {};
206
- const validateForm = async (validatorName, key, value, schema, uiSchema) => {
207
- const validator = validators[validatorName];
208
- if (validator) {
209
- const fieldValidation = createFieldValidation();
210
- try {
211
- await validator(value, fieldValidation, {
212
- ...context,
213
- formData,
214
- schema,
215
- uiSchema
216
- });
217
- } catch (ex) {
218
- fieldValidation.addError(ex.message);
219
- }
220
- formValidation[key] = fieldValidation;
221
- }
222
- };
223
- for (const [key, value] of Object.entries(current)) {
224
- const pointer = `${pathPrefix}/${key}`;
225
- const definitionInSchema = parsedSchema.getSchema({
226
- pointer,
227
- data: formData
228
- });
229
- if (!definitionInSchema) {
230
- continue;
231
- }
232
- if (isJsonError(definitionInSchema)) {
233
- throw new Error(definitionInSchema.message);
234
- }
235
- const { schema, uiSchema } = extractSchemaFromStep(
236
- definitionInSchema
237
- );
238
- const hasItems = definitionInSchema && definitionInSchema.items;
239
- const doValidateItem = async (propValue, itemSchema, itemUiSchema) => {
240
- await validateForm(
241
- propValue["ui:field"],
242
- key,
243
- value,
244
- itemSchema,
245
- itemUiSchema
246
- );
247
- };
248
- const doValidate = async (propValue) => {
249
- if ("ui:field" in propValue) {
250
- const { schema: itemsSchema, uiSchema: itemsUiSchema } = extractSchemaFromStep(definitionInSchema.items);
251
- await doValidateItem(propValue, itemsSchema, itemsUiSchema);
252
- }
253
- };
254
- if ("ui:field" in definitionInSchema) {
255
- await doValidateItem(definitionInSchema, schema, uiSchema);
256
- } else if (hasItems && "ui:field" in definitionInSchema.items) {
257
- await doValidate(definitionInSchema.items);
258
- } else if (hasItems && definitionInSchema.items.type === "object") {
259
- const properties = (_b = (_a = definitionInSchema.items) == null ? void 0 : _a.properties) != null ? _b : [];
260
- for (const [, propValue] of Object.entries(properties)) {
261
- await doValidate(propValue);
262
- }
263
- } else if (isObject(value)) {
264
- formValidation[key] = await validate(formData, pointer, value);
265
- }
266
- }
267
- return formValidation;
268
- }
269
- return async (formData) => {
270
- return await validate(formData);
271
- };
272
- };
273
-
274
- const ReviewState = (props) => {
275
- const reviewData = Object.fromEntries(
276
- Object.entries(props.formState).map(([key, value]) => {
277
- var _a;
278
- for (const step of props.schemas) {
279
- const parsedSchema = new Draft07(step.mergedSchema);
280
- const definitionInSchema = parsedSchema.getSchema({
281
- pointer: `#/${key}`,
282
- data: props.formState
283
- });
284
- if (definitionInSchema) {
285
- const backstageReviewOptions = (_a = definitionInSchema["ui:backstage"]) == null ? void 0 : _a.review;
286
- if (backstageReviewOptions) {
287
- if (backstageReviewOptions.mask) {
288
- return [key, backstageReviewOptions.mask];
289
- }
290
- if (backstageReviewOptions.show === false) {
291
- return [];
292
- }
293
- }
294
- if (definitionInSchema["ui:widget"] === "password") {
295
- return [key, "******"];
296
- }
297
- if (definitionInSchema.enum && definitionInSchema.enumNames) {
298
- return [
299
- key,
300
- definitionInSchema.enumNames[definitionInSchema.enum.indexOf(value)] || value
301
- ];
302
- }
303
- }
304
- }
305
- return [key, value];
306
- }).filter((prop) => prop.length > 0)
307
- );
308
- return /* @__PURE__ */ React.createElement(StructuredMetadataTable, { metadata: reviewData });
309
- };
310
-
311
- const useTemplateSchema = (manifest) => {
312
- const featureFlags = useApi(featureFlagsApiRef);
313
- const steps = manifest.steps.map(({ title, description, schema }) => ({
314
- title,
315
- description,
316
- mergedSchema: schema,
317
- ...extractSchemaFromStep(schema)
318
- }));
319
- const returningSteps = steps.filter((step) => {
320
- var _a;
321
- const stepFeatureFlag = (_a = step.uiSchema["ui:backstage"]) == null ? void 0 : _a.featureFlag;
322
- return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
323
- }).map((step) => {
324
- var _a, _b, _c, _d;
325
- const strippedSchema = {
326
- ...step,
327
- schema: {
328
- ...step.schema,
329
- // Title is rendered at the top of the page, so let's ignore this from jsonschemaform
330
- title: void 0
331
- }
332
- };
333
- if (((_a = step.schema) == null ? void 0 : _a.properties) || !((_b = step.schema) == null ? void 0 : _b.dependencies)) {
334
- strippedSchema.schema.properties = Object.fromEntries(
335
- Object.entries((_d = (_c = step.schema) == null ? void 0 : _c.properties) != null ? _d : {}).filter(
336
- ([key]) => {
337
- var _a2, _b2;
338
- const stepFeatureFlag = (_b2 = (_a2 = step.uiSchema[key]) == null ? void 0 : _a2["ui:backstage"]) == null ? void 0 : _b2.featureFlag;
339
- return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
340
- }
341
- )
342
- );
343
- }
344
- return strippedSchema;
345
- });
346
- return {
347
- presentation: manifest.presentation,
348
- steps: returningSteps
349
- };
350
- };
351
-
352
- const useFormDataFromQuery = (initialState) => {
353
- return useState(() => {
354
- if (initialState) {
355
- return initialState;
356
- }
357
- const query = qs.parse(window.location.search, {
358
- ignoreQueryPrefix: true
359
- });
360
- try {
361
- return JSON.parse(query.formData);
362
- } catch (e) {
363
- return {};
364
- }
365
- });
366
- };
367
-
368
- const useTemplateParameterSchema = (templateRef) => {
369
- const scaffolderApi = useApi(scaffolderApiRef);
370
- const { value, loading, error } = useAsync(
371
- () => scaffolderApi.getTemplateParameterSchema(templateRef),
372
- [scaffolderApi, templateRef]
373
- );
374
- return {
375
- manifest: value,
376
- loading,
377
- error
378
- };
379
- };
380
-
381
- const useFilteredSchemaProperties = (manifest) => {
382
- const featureFlagKey = "backstage:featureFlag";
383
- const featureFlagApi = useApi(featureFlagsApiRef);
384
- if (!manifest) {
385
- return void 0;
386
- }
387
- const filteredSteps = manifest == null ? void 0 : manifest.steps.filter((step) => {
388
- const featureFlag = step.schema[featureFlagKey];
389
- return typeof featureFlag !== "string" || featureFlagApi.isActive(featureFlag);
390
- }).map((step) => {
391
- var _a;
392
- const filteredStep = cloneDeep(step);
393
- const removedPropertyKeys = [];
394
- if (filteredStep.schema.properties) {
395
- filteredStep.schema.properties = Object.fromEntries(
396
- Object.entries(filteredStep.schema.properties).filter(
397
- ([key, value]) => {
398
- if (value[featureFlagKey]) {
399
- if (featureFlagApi.isActive(value[featureFlagKey])) {
400
- return true;
401
- }
402
- removedPropertyKeys.push(key);
403
- return false;
404
- }
405
- return true;
406
- }
407
- )
408
- );
409
- filteredStep.schema.required = Array.isArray(
410
- filteredStep.schema.required
411
- ) ? (_a = filteredStep.schema.required) == null ? void 0 : _a.filter(
412
- (r) => !removedPropertyKeys.includes(r)
413
- ) : filteredStep.schema.required;
414
- }
415
- return filteredStep;
416
- });
417
- return { ...manifest, steps: filteredSteps };
418
- };
419
-
420
- const useTransformSchemaToProps = (step, options = {}) => {
421
- var _a;
422
- const { layouts = [] } = options;
423
- const objectFieldTemplate = step == null ? void 0 : step.uiSchema["ui:ObjectFieldTemplate"];
424
- if (typeof objectFieldTemplate !== "string") {
425
- return step;
426
- }
427
- const Layout = (_a = layouts.find(
428
- (layout) => layout.name === objectFieldTemplate
429
- )) == null ? void 0 : _a.component;
430
- if (!Layout) {
431
- return step;
432
- }
433
- return {
434
- ...step,
435
- uiSchema: {
436
- ...step.uiSchema,
437
- ["ui:ObjectFieldTemplate"]: Layout
438
- }
439
- };
440
- };
441
-
442
- const DescriptionField = ({ description }) => description && /* @__PURE__ */ React.createElement(MarkdownContent, { content: description, linkTarget: "_blank" });
443
-
444
- var FieldOverrides = /*#__PURE__*/Object.freeze({
445
- __proto__: null,
446
- DescriptionField: DescriptionField
447
- });
448
-
449
- const useStyles$c = makeStyles((theme) => ({
450
- markdownDescription: {
451
- fontSize: theme.typography.caption.fontSize,
452
- margin: 0,
453
- color: theme.palette.text.secondary,
454
- "& :first-child": {
455
- margin: 0,
456
- marginTop: "3px"
457
- // to keep the standard browser padding
458
- }
459
- }
460
- }));
461
- const ScaffolderField = (props) => {
462
- const {
463
- children,
464
- displayLabel = true,
465
- rawErrors = [],
466
- errors,
467
- help,
468
- rawDescription,
469
- required,
470
- disabled
471
- } = props;
472
- const classes = useStyles$c();
473
- return /* @__PURE__ */ React.createElement(
474
- FormControl,
475
- {
476
- fullWidth: true,
477
- error: rawErrors.length ? true : false,
478
- required,
479
- disabled
480
- },
481
- children,
482
- displayLabel && rawDescription ? /* @__PURE__ */ React.createElement(
483
- MarkdownContent,
484
- {
485
- content: rawDescription,
486
- className: classes.markdownDescription
487
- }
488
- ) : null,
489
- errors,
490
- help
491
- );
492
- };
493
-
494
- const FieldTemplate = (props) => {
495
- const {
496
- id,
497
- children,
498
- classNames,
499
- style,
500
- disabled,
501
- displayLabel,
502
- hidden,
503
- label,
504
- onDropPropertyClick,
505
- onKeyChange,
506
- readonly,
507
- required,
508
- rawErrors = [],
509
- errors,
510
- help,
511
- rawDescription,
512
- schema,
513
- uiSchema,
514
- registry
515
- } = props;
516
- const uiOptions = getUiOptions(uiSchema);
517
- const WrapIfAdditionalTemplate = getTemplate("WrapIfAdditionalTemplate", registry, uiOptions);
518
- if (hidden) {
519
- return /* @__PURE__ */ React.createElement("div", { style: { display: "none" } }, children);
520
- }
521
- return /* @__PURE__ */ React.createElement(
522
- WrapIfAdditionalTemplate,
523
- {
524
- classNames,
525
- style,
526
- disabled,
527
- id,
528
- label,
529
- onDropPropertyClick,
530
- onKeyChange,
531
- readonly,
532
- required,
533
- schema,
534
- uiSchema,
535
- registry
536
- },
537
- /* @__PURE__ */ React.createElement(
538
- ScaffolderField,
539
- {
540
- displayLabel,
541
- rawErrors,
542
- help,
543
- disabled,
544
- rawDescription,
545
- errors,
546
- required
547
- },
548
- children
549
- )
550
- );
551
- };
552
-
553
- const useStyles$b = makeStyles((theme) => ({
554
- markdownDescription: {
555
- fontSize: theme.typography.caption.fontSize,
556
- margin: 0,
557
- color: theme.palette.text.secondary,
558
- "& :first-child": {
559
- margin: 0,
560
- marginTop: "3px"
561
- // to keep the standard browser padding
562
- }
563
- }
564
- }));
565
- const DescriptionFieldTemplate = (props) => {
566
- const { id, description } = props;
567
- const classes = useStyles$b();
568
- if (description) {
569
- if (typeof description === "string") {
570
- return /* @__PURE__ */ React.createElement(
571
- MarkdownContent,
572
- {
573
- content: description,
574
- className: classes.markdownDescription
575
- }
576
- );
577
- }
578
- return /* @__PURE__ */ React.createElement(Typography, { id, variant: "subtitle2", style: { marginTop: "5px" } }, description);
579
- }
580
- return null;
581
- };
582
-
583
- const WrappedForm = withTheme(Theme);
584
- const Form = (props) => {
585
- const wrappedFields = React.useMemo(
586
- () => {
587
- var _a;
588
- return Object.fromEntries(
589
- Object.entries((_a = props.fields) != null ? _a : {}).map(([key, Component]) => [
590
- key,
591
- (wrapperProps) => {
592
- var _a2, _b;
593
- return /* @__PURE__ */ React.createElement(
594
- Component,
595
- {
596
- ...wrapperProps,
597
- uiSchema: (_a2 = wrapperProps.uiSchema) != null ? _a2 : {},
598
- formData: wrapperProps.formData,
599
- rawErrors: (_b = wrapperProps.rawErrors) != null ? _b : []
600
- }
601
- );
602
- }
603
- ])
604
- );
605
- },
606
- [props.fields]
607
- );
608
- const templates = React.useMemo(
609
- () => ({
610
- FieldTemplate,
611
- DescriptionFieldTemplate,
612
- ...props.templates
613
- }),
614
- [props.templates]
615
- );
616
- return /* @__PURE__ */ React.createElement(WrappedForm, { ...props, templates, fields: wrappedFields });
617
- };
618
-
619
- const useStyles$a = makeStyles(
620
- (_theme) => createStyles({
621
- list: {
622
- width: "100%"
623
- },
624
- text: {
625
- textWrap: "wrap"
626
- }
627
- })
628
- );
629
- const ErrorListTemplate = ({ errors }) => {
630
- const classes = useStyles$a();
631
- return /* @__PURE__ */ React.createElement(Paper, null, /* @__PURE__ */ React.createElement(List, { dense: true, className: classes.list }, errors.map((error, index) => /* @__PURE__ */ React.createElement(ListItem, { key: index }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(ErrorIcon, { color: "error" })), /* @__PURE__ */ React.createElement(
632
- ListItemText,
633
- {
634
- classes: { primary: classes.text },
635
- primary: error.stack
636
- }
637
- )))));
638
- };
639
-
640
- const useStyles$9 = makeStyles((theme) => ({
641
- backButton: {
642
- marginRight: theme.spacing(1)
643
- },
644
- footer: {
645
- display: "flex",
646
- flexDirection: "row",
647
- justifyContent: "right",
648
- marginTop: theme.spacing(2)
649
- },
650
- formWrapper: {
651
- padding: theme.spacing(2)
652
- }
653
- }));
654
- const Stepper = (stepperProps) => {
655
- var _a, _b, _c, _d, _e, _f, _g;
656
- const { layouts = [], components = {}, ...props } = stepperProps;
657
- const {
658
- ReviewStateComponent = ReviewState,
659
- ReviewStepComponent,
660
- backButtonText = "Back",
661
- createButtonText = "Create",
662
- reviewButtonText = "Review"
663
- } = components;
664
- const analytics = useAnalytics();
665
- const { presentation, steps } = useTemplateSchema(props.manifest);
666
- const apiHolder = useApiHolder();
667
- const [activeStep, setActiveStep] = useState(0);
668
- const [isValidating, setIsValidating] = useState(false);
669
- const [formState, setFormState] = useFormDataFromQuery(props.initialState);
670
- const [errors, setErrors] = useState();
671
- const styles = useStyles$9();
672
- const extensions = useMemo(() => {
673
- return Object.fromEntries(
674
- props.extensions.map(({ name, component }) => [name, component])
675
- );
676
- }, [props.extensions]);
677
- const fields = useMemo(
678
- () => ({ ...FieldOverrides, ...extensions }),
679
- [extensions]
680
- );
681
- const validators = useMemo(() => {
682
- return Object.fromEntries(
683
- props.extensions.map(({ name, validation: validation2 }) => [name, validation2])
684
- );
685
- }, [props.extensions]);
686
- const validation = useMemo(() => {
687
- var _a2;
688
- return createAsyncValidators((_a2 = steps[activeStep]) == null ? void 0 : _a2.mergedSchema, validators, {
689
- apiHolder
690
- });
691
- }, [steps, activeStep, validators, apiHolder]);
692
- const handleBack = () => {
693
- setActiveStep((prevActiveStep) => prevActiveStep - 1);
694
- };
695
- const handleChange = useCallback(
696
- (e) => setFormState((current) => ({ ...current, ...e.formData })),
697
- [setFormState]
698
- );
699
- const handleCreate = useCallback(() => {
700
- props.onCreate(formState);
701
- }, [props, formState]);
702
- const currentStep = useTransformSchemaToProps(steps[activeStep], { layouts });
703
- const handleNext = async ({
704
- formData = {}
705
- }) => {
706
- setErrors(void 0);
707
- setIsValidating(true);
708
- const returnedValidation = await validation(formData);
709
- setIsValidating(false);
710
- if (hasErrors(returnedValidation)) {
711
- setErrors(returnedValidation);
712
- } else {
713
- setErrors(void 0);
714
- setActiveStep((prevActiveStep) => {
715
- const stepNum = prevActiveStep + 1;
716
- analytics.captureEvent("click", `Next Step (${stepNum})`);
717
- return stepNum;
718
- });
719
- }
720
- setFormState((current) => ({ ...current, ...formData }));
721
- };
722
- const backLabel = (_b = (_a = presentation == null ? void 0 : presentation.buttonLabels) == null ? void 0 : _a.backButtonText) != null ? _b : backButtonText;
723
- const createLabel = (_d = (_c = presentation == null ? void 0 : presentation.buttonLabels) == null ? void 0 : _c.createButtonText) != null ? _d : createButtonText;
724
- const reviewLabel = (_f = (_e = presentation == null ? void 0 : presentation.buttonLabels) == null ? void 0 : _e.reviewButtonText) != null ? _f : reviewButtonText;
725
- return /* @__PURE__ */ React.createElement(React.Fragment, null, isValidating && /* @__PURE__ */ React.createElement(LinearProgress, { variant: "indeterminate" }), /* @__PURE__ */ React.createElement(
726
- MuiStepper,
727
- {
728
- activeStep,
729
- alternativeLabel: true,
730
- variant: "elevation",
731
- style: { overflowX: "auto" }
732
- },
733
- steps.map((step, index) => {
734
- const isAllowedLabelClick = activeStep > index;
735
- return /* @__PURE__ */ React.createElement(MuiStep, { key: index }, /* @__PURE__ */ React.createElement(
736
- MuiStepLabel,
737
- {
738
- "aria-label": `Step ${index + 1}`,
739
- style: { cursor: isAllowedLabelClick ? "pointer" : "default" },
740
- onClick: () => {
741
- if (isAllowedLabelClick)
742
- setActiveStep(index);
743
- }
744
- },
745
- step.title
746
- ));
747
- }),
748
- /* @__PURE__ */ React.createElement(MuiStep, null, /* @__PURE__ */ React.createElement(MuiStepLabel, null, "Review"))
749
- ), /* @__PURE__ */ React.createElement("div", { className: styles.formWrapper }, activeStep < steps.length ? /* @__PURE__ */ React.createElement(
750
- Form,
751
- {
752
- validator,
753
- extraErrors: errors,
754
- formData: formState,
755
- formContext: { formData: formState },
756
- schema: currentStep.schema,
757
- uiSchema: currentStep.uiSchema,
758
- onSubmit: handleNext,
759
- fields,
760
- showErrorList: "top",
761
- templates: { ErrorListTemplate },
762
- onChange: handleChange,
763
- experimental_defaultFormStateBehavior: {
764
- allOf: "populateDefaults"
765
- },
766
- ...(_g = props.formProps) != null ? _g : {}
767
- },
768
- /* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement(
769
- Button,
770
- {
771
- onClick: handleBack,
772
- className: styles.backButton,
773
- disabled: activeStep < 1 || isValidating
774
- },
775
- backLabel
776
- ), /* @__PURE__ */ React.createElement(
777
- Button,
778
- {
779
- variant: "contained",
780
- color: "primary",
781
- type: "submit",
782
- disabled: isValidating
783
- },
784
- activeStep === steps.length - 1 ? reviewLabel : "Next"
785
- ))
786
- ) : (
787
- // TODO: potentially move away from this pattern, deprecate?
788
- ReviewStepComponent ? /* @__PURE__ */ React.createElement(
789
- ReviewStepComponent,
790
- {
791
- disableButtons: isValidating,
792
- formData: formState,
793
- handleBack,
794
- handleReset: () => {
795
- },
796
- steps,
797
- handleCreate
798
- }
799
- ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ReviewStateComponent, { formState, schemas: steps }), /* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement(
800
- Button,
801
- {
802
- onClick: handleBack,
803
- className: styles.backButton,
804
- disabled: activeStep < 1
805
- },
806
- "Back"
807
- ), /* @__PURE__ */ React.createElement(
808
- Button,
809
- {
810
- variant: "contained",
811
- color: "primary",
812
- onClick: handleCreate
813
- },
814
- createLabel
815
- )))
816
- )));
817
- };
818
-
819
- const useStyles$8 = makeStyles(() => ({
820
- header: {
821
- backgroundImage: ({ cardBackgroundImage }) => cardBackgroundImage,
822
- color: ({ cardFontColor }) => cardFontColor
823
- },
824
- subtitleWrapper: {
825
- display: "flex",
826
- justifyContent: "space-between"
827
- }
828
- }));
829
- const CardHeader = (props) => {
830
- const {
831
- template: {
832
- metadata: { title, name },
833
- spec: { type }
834
- }
835
- } = props;
836
- const { getPageTheme } = useTheme();
837
- const themeForType = getPageTheme({ themeId: type });
838
- const styles = useStyles$8({
839
- cardFontColor: themeForType.fontColor,
840
- cardBackgroundImage: themeForType.backgroundImage
841
- });
842
- const SubtitleComponent = /* @__PURE__ */ React.createElement("div", { className: styles.subtitleWrapper }, /* @__PURE__ */ React.createElement("div", null, type), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(FavoriteEntity, { entity: props.template, style: { padding: 0 } })));
843
- return /* @__PURE__ */ React.createElement(
844
- ItemCardHeader,
845
- {
846
- title: title != null ? title : name,
847
- subtitle: SubtitleComponent,
848
- classes: { root: styles.header }
849
- }
850
- );
851
- };
852
-
853
- const useStyles$7 = makeStyles(() => ({
854
- linkText: {
855
- display: "inline-flex",
856
- alignItems: "center"
857
- }
858
- }));
859
- const CardLink = ({ icon: Icon, text, url }) => {
860
- const styles = useStyles$7();
861
- return /* @__PURE__ */ React.createElement("div", { className: styles.linkText }, /* @__PURE__ */ React.createElement(Icon, { fontSize: "small" }), /* @__PURE__ */ React.createElement(Link, { style: { marginLeft: "8px" }, to: url }, text || url));
862
- };
863
-
864
- const useStyles$6 = makeStyles((theme) => ({
865
- box: {
866
- overflow: "hidden",
867
- textOverflow: "ellipsis",
868
- display: "-webkit-box",
869
- "-webkit-line-clamp": 10,
870
- "-webkit-box-orient": "vertical"
871
- },
872
- markdown: {
873
- /** to make the styles for React Markdown not leak into the description */
874
- "& :first-child": {
875
- margin: 0
876
- }
877
- },
878
- label: {
879
- color: theme.palette.text.secondary,
880
- textTransform: "uppercase",
881
- fontWeight: "bold",
882
- letterSpacing: 0.5,
883
- lineHeight: 1,
884
- fontSize: "0.75rem"
885
- },
886
- footer: {
887
- display: "flex",
888
- justifyContent: "space-between",
889
- flex: 1,
890
- alignItems: "center"
891
- },
892
- ownedBy: {
893
- display: "flex",
894
- alignItems: "center",
895
- flex: 1,
896
- color: theme.palette.link
897
- }
898
- }));
899
- const TemplateCard = (props) => {
900
- var _a, _b, _c, _d, _e, _f, _g;
901
- const { template } = props;
902
- const styles = useStyles$6();
903
- const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY);
904
- const app = useApp();
905
- const iconResolver = (key) => {
906
- var _a2;
907
- return key ? (_a2 = app.getSystemIcon(key)) != null ? _a2 : LanguageIcon : LanguageIcon;
908
- };
909
- const hasTags = !!((_a = template.metadata.tags) == null ? void 0 : _a.length);
910
- const hasLinks = !!((_b = props.additionalLinks) == null ? void 0 : _b.length) || !!((_c = template.metadata.links) == null ? void 0 : _c.length);
911
- const displayDefaultDivider = !hasTags && !hasLinks;
912
- return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, { template }), /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Box, { className: styles.box }, /* @__PURE__ */ React.createElement(
913
- MarkdownContent,
914
- {
915
- className: styles.markdown,
916
- content: (_d = template.metadata.description) != null ? _d : "No description"
917
- }
918
- ))), displayDefaultDivider && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Divider, { "data-testid": "template-card-separator" })), hasTags && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Divider, { "data-testid": "template-card-separator--tags" })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, (_e = template.metadata.tags) == null ? void 0 : _e.map((tag) => /* @__PURE__ */ React.createElement(Grid, { key: `grid-${tag}`, item: true }, /* @__PURE__ */ React.createElement(
919
- Chip,
920
- {
921
- style: { margin: 0 },
922
- size: "small",
923
- label: tag,
924
- key: tag
925
- }
926
- )))))), hasLinks && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Divider, { "data-testid": "template-card-separator--links" })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, (_f = props.additionalLinks) == null ? void 0 : _f.map(({ icon, text, url }, index) => /* @__PURE__ */ React.createElement(Grid, { className: styles.linkText, item: true, xs: 6, key: index }, /* @__PURE__ */ React.createElement(CardLink, { icon, text, url }))), (_g = template.metadata.links) == null ? void 0 : _g.map(
927
- ({ url, icon, title }, index) => /* @__PURE__ */ React.createElement(Grid, { className: styles.linkText, item: true, xs: 6, key: index }, /* @__PURE__ */ React.createElement(
928
- CardLink,
929
- {
930
- icon: iconResolver(icon),
931
- text: title || url,
932
- url
933
- }
934
- ))
935
- )))))), /* @__PURE__ */ React.createElement(CardActions, { style: { padding: "16px", flex: 1, alignItems: "flex-end" } }, /* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement("div", { className: styles.ownedBy }, ownedByRelations.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(UserIcon, { fontSize: "small" }), /* @__PURE__ */ React.createElement(
936
- EntityRefLinks,
937
- {
938
- style: { marginLeft: "8px" },
939
- entityRefs: ownedByRelations,
940
- defaultKind: "Group",
941
- hideIcons: true
942
- }
943
- ))), /* @__PURE__ */ React.createElement(
944
- Button,
945
- {
946
- size: "small",
947
- variant: "outlined",
948
- color: "primary",
949
- onClick: () => {
950
- var _a2;
951
- return (_a2 = props.onSelected) == null ? void 0 : _a2.call(props, template);
952
- }
953
- },
954
- "Choose"
955
- ))));
956
- };
957
-
958
- const TemplateGroup = (props) => {
959
- const {
960
- templates,
961
- title,
962
- components: { CardComponent } = {},
963
- onSelected
964
- } = props;
965
- const titleComponent = typeof title === "string" ? /* @__PURE__ */ React.createElement(ContentHeader, { title }) : title;
966
- if (templates.length === 0) {
967
- return null;
968
- }
969
- const Card = CardComponent || TemplateCard;
970
- return /* @__PURE__ */ React.createElement(Content, null, titleComponent, /* @__PURE__ */ React.createElement(ItemCardGrid, null, templates.map(({ template, additionalLinks }) => /* @__PURE__ */ React.createElement(
971
- Card,
972
- {
973
- key: stringifyEntityRef(template),
974
- additionalLinks,
975
- template,
976
- onSelected
977
- }
978
- ))));
979
- };
980
-
981
- const TemplateGroups = (props) => {
982
- const { loading, error, entities } = useEntityList();
983
- const { groups, templateFilter, TemplateCardComponent, onTemplateSelected } = props;
984
- const errorApi = useApi(errorApiRef);
985
- const onSelected = useCallback(
986
- (template) => {
987
- onTemplateSelected == null ? void 0 : onTemplateSelected(template);
988
- },
989
- [onTemplateSelected]
990
- );
991
- if (loading) {
992
- return /* @__PURE__ */ React.createElement(Progress, null);
993
- }
994
- if (error) {
995
- errorApi.post(error);
996
- return null;
997
- }
998
- if (!entities || !entities.length) {
999
- return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link, { to: "https://backstage.io/docs/features/software-templates/adding-templates" }, "adding templates"), ".");
1000
- }
1001
- return /* @__PURE__ */ React.createElement(React.Fragment, null, groups.map(({ title, filter }, index) => {
1002
- const templates = entities.filter(isTemplateEntityV1beta3).filter((e) => templateFilter ? templateFilter(e) : true).filter(filter).map((template) => {
1003
- var _a, _b;
1004
- const additionalLinks = (_b = (_a = props.additionalLinksForEntity) == null ? void 0 : _a.call(props, template)) != null ? _b : [];
1005
- return {
1006
- template,
1007
- additionalLinks
1008
- };
1009
- });
1010
- return /* @__PURE__ */ React.createElement(
1011
- TemplateGroup,
1012
- {
1013
- key: index,
1014
- templates,
1015
- title,
1016
- components: { CardComponent: TemplateCardComponent },
1017
- onSelected
1018
- }
1019
- );
1020
- }));
1021
- };
1022
-
1023
- const useTemplateTimeSavedMinutes = (templateRef) => {
1024
- const catalogApi = useApi(catalogApiRef);
1025
- const { value: timeSavedMinutes } = useAsync$1(async () => {
1026
- var _a;
1027
- const entity = await catalogApi.getEntityByRef(templateRef);
1028
- const timeSaved = (_a = entity == null ? void 0 : entity.metadata.annotations) == null ? void 0 : _a["backstage.io/time-saved"];
1029
- if (!entity || !timeSaved) {
1030
- return void 0;
1031
- }
1032
- const durationMs = Duration.fromISO(timeSaved).as("minutes");
1033
- if (Number.isNaN(durationMs)) {
1034
- return void 0;
1035
- }
1036
- return durationMs;
1037
- }, [catalogApi, templateRef]);
1038
- return timeSavedMinutes;
1039
- };
1040
-
1041
- const useStyles$5 = makeStyles({
1042
- markdown: {
1043
- /** to make the styles for React Markdown not leak into the description */
1044
- "& :first-child": {
1045
- marginTop: 0
1046
- },
1047
- "& :last-child": {
1048
- marginBottom: 0
1049
- }
1050
- }
1051
- });
1052
- const Workflow = (workflowProps) => {
1053
- var _a;
1054
- const { title, description, namespace, templateName, onCreate, ...props } = workflowProps;
1055
- const analytics = useAnalytics();
1056
- const styles = useStyles$5();
1057
- const templateRef = stringifyEntityRef({
1058
- kind: "Template",
1059
- namespace,
1060
- name: templateName
1061
- });
1062
- const errorApi = useApi(errorApiRef);
1063
- const { loading, manifest, error } = useTemplateParameterSchema(templateRef);
1064
- const sortedManifest = useFilteredSchemaProperties(manifest);
1065
- const minutesSaved = useTemplateTimeSavedMinutes(templateRef);
1066
- const workflowOnCreate = useCallback(
1067
- async (formState) => {
1068
- var _a2;
1069
- onCreate(formState);
1070
- const name = typeof formState.name === "string" ? formState.name : void 0;
1071
- analytics.captureEvent("create", (_a2 = name != null ? name : templateName) != null ? _a2 : "unknown", {
1072
- value: minutesSaved
1073
- });
1074
- },
1075
- [onCreate, analytics, templateName, minutesSaved]
1076
- );
1077
- useEffect(() => {
1078
- if (error) {
1079
- errorApi.post(new Error(`Failed to load template, ${error}`));
1080
- }
1081
- }, [error, errorApi]);
1082
- if (error) {
1083
- return props.onError(error);
1084
- }
1085
- return /* @__PURE__ */ React.createElement(Content, null, loading && /* @__PURE__ */ React.createElement(Progress, null), sortedManifest && /* @__PURE__ */ React.createElement(
1086
- InfoCard,
1087
- {
1088
- title: title != null ? title : sortedManifest.title,
1089
- subheader: /* @__PURE__ */ React.createElement(
1090
- MarkdownContent,
1091
- {
1092
- className: styles.markdown,
1093
- content: (_a = description != null ? description : sortedManifest.description) != null ? _a : "No description"
1094
- }
1095
- ),
1096
- noPadding: true,
1097
- titleTypographyProps: { component: "h2" }
1098
- },
1099
- /* @__PURE__ */ React.createElement(
1100
- Stepper,
1101
- {
1102
- manifest: sortedManifest,
1103
- onCreate: workflowOnCreate,
1104
- ...props
1105
- }
1106
- )
1107
- ));
1108
- };
1109
- const EmbeddableWorkflow = (props) => /* @__PURE__ */ React.createElement(SecretsContextProvider, null, /* @__PURE__ */ React.createElement(Workflow, { ...props }));
1110
-
1111
- const useStyles$4 = makeStyles({
1112
- root: {
1113
- "&:hover": {
1114
- textDecoration: "none"
1115
- }
1116
- }
1117
- });
1118
- const LinkOutputs = (props) => {
1119
- const { links = [] } = props.output;
1120
- const classes = useStyles$4();
1121
- const app = useApp();
1122
- const entityRoute = useRouteRef(entityRouteRef);
1123
- const iconResolver = (key) => {
1124
- var _a;
1125
- return (_a = app.getSystemIcon(key)) != null ? _a : LinkIcon;
1126
- };
1127
- return /* @__PURE__ */ React.createElement(React.Fragment, null, links.filter(({ url, entityRef }) => url || entityRef).map(({ url, entityRef, title, icon }) => {
1128
- if (entityRef) {
1129
- const entityName = parseEntityRef(entityRef);
1130
- const target = entityRoute(entityName);
1131
- return { title, icon, url: target };
1132
- }
1133
- return { title, icon, url };
1134
- }).map(({ url, title, icon }, i) => {
1135
- const Icon = iconResolver(icon);
1136
- return /* @__PURE__ */ React.createElement(Link, { to: url, key: i, classes: { root: classes.root } }, /* @__PURE__ */ React.createElement(Button, { startIcon: /* @__PURE__ */ React.createElement(Icon, null), component: "div", color: "primary" }, title));
1137
- }));
1138
- };
1139
-
1140
- const TextOutputs = (props) => {
1141
- const {
1142
- output: { text = [] },
1143
- index,
1144
- setIndex
1145
- } = props;
1146
- const app = useApp();
1147
- const iconResolver = (key) => {
1148
- var _a;
1149
- return (_a = app.getSystemIcon(key)) != null ? _a : DescriptionIcon;
1150
- };
1151
- return /* @__PURE__ */ React.createElement(React.Fragment, null, text.filter(({ content }) => content !== void 0).map(({ title, icon }, i) => {
1152
- const Icon = iconResolver(icon);
1153
- return /* @__PURE__ */ React.createElement(
1154
- Button,
1155
- {
1156
- key: i,
1157
- startIcon: /* @__PURE__ */ React.createElement(Icon, null),
1158
- component: "div",
1159
- color: "primary",
1160
- onClick: () => {
1161
- if (index !== i) {
1162
- setIndex == null ? void 0 : setIndex(i);
1163
- }
1164
- },
1165
- variant: index === i ? "outlined" : void 0
1166
- },
1167
- title
1168
- );
1169
- }));
1170
- };
1171
-
1172
- const DefaultTemplateOutputs = (props) => {
1173
- var _a, _b;
1174
- const { output } = props;
1175
- const [textOutputIndex, setTextOutputIndex] = useState(
1176
- void 0
1177
- );
1178
- useEffect(() => {
1179
- if (textOutputIndex === void 0 && (output == null ? void 0 : output.text)) {
1180
- const defaultIndex = output.text.findIndex(
1181
- (t) => t.default
1182
- );
1183
- setTextOutputIndex(defaultIndex >= 0 ? defaultIndex : 0);
1184
- }
1185
- }, [textOutputIndex, output]);
1186
- const textOutput = useMemo(
1187
- () => {
1188
- var _a2;
1189
- return textOutputIndex !== void 0 ? (_a2 = output == null ? void 0 : output.text) == null ? void 0 : _a2[textOutputIndex] : null;
1190
- },
1191
- [output, textOutputIndex]
1192
- );
1193
- if (!output) {
1194
- return null;
1195
- }
1196
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2 }, /* @__PURE__ */ React.createElement(Paper, null, /* @__PURE__ */ React.createElement(Box, { padding: 2, justifyContent: "center", display: "flex", gridGap: 16 }, /* @__PURE__ */ React.createElement(
1197
- TextOutputs,
1198
- {
1199
- output,
1200
- index: textOutputIndex,
1201
- setIndex: setTextOutputIndex
1202
- }
1203
- ), /* @__PURE__ */ React.createElement(LinkOutputs, { output })))), textOutput ? /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2 }, /* @__PURE__ */ React.createElement(
1204
- InfoCard,
1205
- {
1206
- title: (_a = textOutput.title) != null ? _a : "Text Output",
1207
- noPadding: true,
1208
- titleTypographyProps: { component: "h2" }
1209
- },
1210
- /* @__PURE__ */ React.createElement(Box, { padding: 2, height: "100%" }, /* @__PURE__ */ React.createElement(MarkdownContent, { content: (_b = textOutput.content) != null ? _b : "" }))
1211
- )) : null);
1212
- };
1213
-
1214
- const useStepIconStyles = makeStyles((theme) => ({
1215
- root: {
1216
- color: theme.palette.text.disabled
1217
- },
1218
- completed: {
1219
- color: theme.palette.status.ok
1220
- },
1221
- error: {
1222
- color: theme.palette.status.error
1223
- }
1224
- }));
1225
- const StepIcon = (props) => {
1226
- const classes = useStepIconStyles();
1227
- const { active, completed, error, skipped } = props;
1228
- const getMiddle = () => {
1229
- if (active) {
1230
- return /* @__PURE__ */ React.createElement(CircularProgress, { size: "20px" });
1231
- }
1232
- if (completed) {
1233
- return /* @__PURE__ */ React.createElement(CheckCircleOutline, null);
1234
- }
1235
- if (error) {
1236
- return /* @__PURE__ */ React.createElement(ErrorOutline, null);
1237
- }
1238
- if (skipped) {
1239
- return /* @__PURE__ */ React.createElement(RemoveCircleOutline, null);
1240
- }
1241
- return /* @__PURE__ */ React.createElement(PanoramaFishEyeIcon, null);
1242
- };
1243
- return /* @__PURE__ */ React.createElement(
1244
- "div",
1245
- {
1246
- className: classNames(classes.root, {
1247
- [classes.completed]: completed,
1248
- [classes.error]: error
1249
- })
1250
- },
1251
- getMiddle()
1252
- );
1253
- };
1254
-
1255
- const StepTime = (props) => {
1256
- const [time, setTime] = useState("");
1257
- const { step } = props;
1258
- const getDelay = () => {
1259
- if (step.startedAt && step.endedAt && time) {
1260
- return null;
1261
- }
1262
- if (step.startedAt && step.endedAt) {
1263
- return 1;
1264
- }
1265
- return 1e3;
1266
- };
1267
- const calculate = useCallback(() => {
1268
- if (!step.startedAt) {
1269
- setTime("");
1270
- return;
1271
- }
1272
- const end = step.endedAt ? DateTime.fromISO(step.endedAt) : DateTime.local();
1273
- const startedAt = DateTime.fromISO(step.startedAt);
1274
- const formatted = Interval.fromDateTimes(startedAt, end).toDuration().valueOf();
1275
- setTime(humanizeDuration(formatted, { round: true }));
1276
- }, [step.endedAt, step.startedAt]);
1277
- useMountEffect(calculate);
1278
- useInterval(calculate, getDelay());
1279
- return /* @__PURE__ */ React.createElement(Typography, { variant: "caption" }, time);
1280
- };
1281
-
1282
- const useStyles$3 = makeStyles((theme) => ({
1283
- failed: {
1284
- backgroundColor: theme.palette.error.main
1285
- },
1286
- success: {
1287
- backgroundColor: theme.palette.success.main
1288
- }
1289
- }));
1290
- const TaskBorder = (props) => {
1291
- const styles = useStyles$3();
1292
- if (!props.isComplete) {
1293
- return /* @__PURE__ */ React.createElement(LinearProgress, { variant: "indeterminate" });
1294
- }
1295
- return /* @__PURE__ */ React.createElement(
1296
- LinearProgress,
1297
- {
1298
- variant: "determinate",
1299
- classes: { bar: props.isError ? styles.failed : styles.success },
1300
- value: 100
1301
- }
1302
- );
1303
- };
1304
-
1305
- const TaskSteps = (props) => {
1306
- var _a, _b;
1307
- return /* @__PURE__ */ React.createElement(Paper, { style: { position: "relative", overflow: "hidden" } }, /* @__PURE__ */ React.createElement(
1308
- TaskBorder,
1309
- {
1310
- isComplete: (_a = props.isComplete) != null ? _a : false,
1311
- isError: (_b = props.isError) != null ? _b : false
1312
- }
1313
- ), /* @__PURE__ */ React.createElement(Box, { padding: 2 }, /* @__PURE__ */ React.createElement(
1314
- MuiStepper,
1315
- {
1316
- activeStep: props.activeStep,
1317
- alternativeLabel: true,
1318
- variant: "elevation",
1319
- style: { overflowX: "auto" }
1320
- },
1321
- props.steps.map((step) => {
1322
- const isCompleted = step.status === "completed";
1323
- const isFailed = step.status === "failed";
1324
- const isActive = step.status === "processing";
1325
- const isSkipped = step.status === "skipped";
1326
- const stepIconProps = {
1327
- completed: isCompleted,
1328
- error: isFailed,
1329
- active: isActive,
1330
- skipped: isSkipped
1331
- };
1332
- return /* @__PURE__ */ React.createElement(MuiStep, { key: step.id }, /* @__PURE__ */ React.createElement(MuiStepButton, null, /* @__PURE__ */ React.createElement(
1333
- MuiStepLabel,
1334
- {
1335
- StepIconProps: stepIconProps,
1336
- StepIconComponent: StepIcon,
1337
- "data-testid": "step-label"
1338
- },
1339
- /* @__PURE__ */ React.createElement(Box, null, step.name),
1340
- !isSkipped && /* @__PURE__ */ React.createElement(StepTime, { step })
1341
- )));
1342
- })
1343
- )));
1344
- };
1345
-
1346
- const useStyles$2 = makeStyles({
1347
- root: {
1348
- width: "100%",
1349
- height: "100%",
1350
- position: "relative"
1351
- }
1352
- });
1353
- const TaskLogStream = (props) => {
1354
- const styles = useStyles$2();
1355
- return /* @__PURE__ */ React.createElement("div", { className: styles.root }, /* @__PURE__ */ React.createElement(
1356
- LogViewer,
1357
- {
1358
- text: Object.values(props.logs).map((l) => l.join("\n")).filter(Boolean).join("\n")
1359
- }
1360
- ));
1361
- };
1362
-
1363
- const icon = /* @__PURE__ */ React.createElement(CheckBoxOutlineBlankIcon, { fontSize: "small" });
1364
- const checkedIcon = /* @__PURE__ */ React.createElement(CheckBoxIcon, { fontSize: "small" });
1365
- const useStyles$1 = makeStyles(
1366
- {
1367
- root: {},
1368
- label: {}
1369
- },
1370
- { name: "ScaffolderReactTemplateCategoryPicker" }
1371
- );
1372
- const TemplateCategoryPicker = () => {
1373
- const classes = useStyles$1();
1374
- const alertApi = useApi(alertApiRef);
1375
- const { error, loading, availableTypes, selectedTypes, setSelectedTypes } = useEntityTypeFilter();
1376
- if (loading)
1377
- return /* @__PURE__ */ React.createElement(Progress, null);
1378
- if (error) {
1379
- alertApi.post({
1380
- message: `Failed to load entity types with error: ${error}`,
1381
- severity: "error"
1382
- });
1383
- return null;
1384
- }
1385
- if (!availableTypes)
1386
- return null;
1387
- return /* @__PURE__ */ React.createElement(Box, { className: classes.root, pb: 1, pt: 1 }, /* @__PURE__ */ React.createElement(
1388
- Typography,
1389
- {
1390
- className: classes.label,
1391
- variant: "button",
1392
- component: "label",
1393
- htmlFor: "categories-picker"
1394
- },
1395
- "Categories"
1396
- ), /* @__PURE__ */ React.createElement(
1397
- Autocomplete,
1398
- {
1399
- multiple: true,
1400
- id: "categories-picker",
1401
- options: availableTypes,
1402
- value: selectedTypes,
1403
- onChange: (_, value) => setSelectedTypes(value),
1404
- renderOption: (option, { selected }) => /* @__PURE__ */ React.createElement(
1405
- FormControlLabel,
1406
- {
1407
- control: /* @__PURE__ */ React.createElement(
1408
- Checkbox,
1409
- {
1410
- icon,
1411
- checkedIcon,
1412
- checked: selected
1413
- }
1414
- ),
1415
- label: capitalize(option)
1416
- }
1417
- ),
1418
- size: "small",
1419
- popupIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null),
1420
- renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, { ...params, variant: "outlined" })
1421
- }
1422
- ));
1423
- };
1424
-
1425
- const useStyles = makeStyles((theme) => ({
1426
- button: {
1427
- color: theme.page.fontColor
1428
- }
1429
- }));
1430
- function ScaffolderPageContextMenu(props) {
1431
- const { onEditorClicked, onActionsClicked, onTasksClicked, onCreateClicked } = props;
1432
- const classes = useStyles();
1433
- const [anchorEl, setAnchorEl] = useState();
1434
- if (!onEditorClicked && !onActionsClicked) {
1435
- return null;
1436
- }
1437
- const onOpen = (event) => {
1438
- setAnchorEl(event.currentTarget);
1439
- };
1440
- const onClose = () => {
1441
- setAnchorEl(void 0);
1442
- };
1443
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1444
- IconButton,
1445
- {
1446
- id: "long-menu",
1447
- "aria-label": "more",
1448
- "aria-controls": "long-menu",
1449
- "aria-expanded": !!anchorEl,
1450
- "aria-haspopup": "true",
1451
- role: "button",
1452
- onClick: onOpen,
1453
- "data-testid": "menu-button",
1454
- color: "inherit",
1455
- className: classes.button
1456
- },
1457
- /* @__PURE__ */ React.createElement(MoreVert, null)
1458
- ), /* @__PURE__ */ React.createElement(
1459
- Popover,
1460
- {
1461
- "aria-labelledby": "long-menu",
1462
- open: Boolean(anchorEl),
1463
- onClose,
1464
- anchorEl,
1465
- anchorOrigin: { vertical: "bottom", horizontal: "right" },
1466
- transformOrigin: { vertical: "top", horizontal: "right" }
1467
- },
1468
- /* @__PURE__ */ React.createElement(MenuList, null, onCreateClicked && /* @__PURE__ */ React.createElement(MenuItem, { onClick: onCreateClicked }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(CreateComponentIcon, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText, { primary: "Create" })), onEditorClicked && /* @__PURE__ */ React.createElement(MenuItem, { onClick: onEditorClicked }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Edit, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText, { primary: "Template Editor" })), onActionsClicked && /* @__PURE__ */ React.createElement(MenuItem, { onClick: onActionsClicked }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(DescriptionIcon, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText, { primary: "Installed Actions" })), onTasksClicked && /* @__PURE__ */ React.createElement(MenuItem, { onClick: onTasksClicked }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(List$1, { fontSize: "small" })), /* @__PURE__ */ React.createElement(ListItemText, { primary: "Task List" })))
1469
- ));
1470
- }
1471
-
1472
- export { DefaultTemplateOutputs, EmbeddableWorkflow, Form, ReviewState, ScaffolderField, ScaffolderPageContextMenu, Stepper, TaskLogStream, TaskSteps, TemplateCard, TemplateCategoryPicker, TemplateGroup, TemplateGroups, Workflow, createAsyncValidators, createFieldValidation, extractSchemaFromStep, useFilteredSchemaProperties, useFormDataFromQuery, useTemplateParameterSchema, useTemplateSchema };
1
+ export { Stepper } from './next/components/Stepper/Stepper.esm.js';
2
+ export { createAsyncValidators } from './next/components/Stepper/createAsyncValidators.esm.js';
3
+ export { TemplateCard } from './next/components/TemplateCard/TemplateCard.esm.js';
4
+ export { ReviewState } from './next/components/ReviewState/ReviewState.esm.js';
5
+ export { TemplateGroup } from './next/components/TemplateGroup/TemplateGroup.esm.js';
6
+ export { TemplateGroups } from './next/components/TemplateGroups/TemplateGroups.esm.js';
7
+ export { EmbeddableWorkflow, Workflow } from './next/components/Workflow/Workflow.esm.js';
8
+ export { DefaultTemplateOutputs } from './next/components/TemplateOutputs/DefaultTemplateOutputs.esm.js';
9
+ export { Form } from './next/components/Form/Form.esm.js';
10
+ export { TaskSteps } from './next/components/TaskSteps/TaskSteps.esm.js';
11
+ export { TaskLogStream } from './next/components/TaskLogStream/TaskLogStream.esm.js';
12
+ export { TemplateCategoryPicker } from './next/components/TemplateCategoryPicker/TemplateCategoryPicker.esm.js';
13
+ export { ScaffolderPageContextMenu } from './next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.esm.js';
14
+ export { ScaffolderField } from './next/components/ScaffolderField/ScaffolderField.esm.js';
15
+ export { createFieldValidation, extractSchemaFromStep } from './next/lib/schema.esm.js';
16
+ export { useFormDataFromQuery } from './next/hooks/useFormDataFromQuery.esm.js';
17
+ export { useTemplateSchema } from './next/hooks/useTemplateSchema.esm.js';
18
+ export { useTemplateParameterSchema } from './next/hooks/useTemplateParameterSchema.esm.js';
19
+ export { useFilteredSchemaProperties } from './next/hooks/useFilteredSchemaProperties.esm.js';
1473
20
  //# sourceMappingURL=alpha.esm.js.map