@backstage/plugin-catalog 2.0.5 → 2.0.6-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 (32) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/alpha/pages.esm.js +50 -5
  3. package/dist/alpha/pages.esm.js.map +1 -1
  4. package/dist/alpha/translation.esm.js +11 -0
  5. package/dist/alpha/translation.esm.js.map +1 -1
  6. package/dist/alpha.d.ts +57 -2
  7. package/dist/alpha.esm.js +1 -0
  8. package/dist/alpha.esm.js.map +1 -1
  9. package/dist/components/CatalogExportButton/CatalogExportButton.esm.js +163 -0
  10. package/dist/components/CatalogExportButton/CatalogExportButton.esm.js.map +1 -0
  11. package/dist/components/CatalogExportButton/file-download/downloadFile.esm.js +52 -0
  12. package/dist/components/CatalogExportButton/file-download/downloadFile.esm.js.map +1 -0
  13. package/dist/components/CatalogExportButton/file-download/serializeEntities.esm.js +47 -0
  14. package/dist/components/CatalogExportButton/file-download/serializeEntities.esm.js.map +1 -0
  15. package/dist/components/CatalogExportButton/file-download/toStreamRequest.esm.js +52 -0
  16. package/dist/components/CatalogExportButton/file-download/toStreamRequest.esm.js.map +1 -0
  17. package/dist/components/CatalogExportButton/file-download/useStreamingExport.esm.js +112 -0
  18. package/dist/components/CatalogExportButton/file-download/useStreamingExport.esm.js.map +1 -0
  19. package/dist/components/CatalogPage/DefaultCatalogPage.esm.js +20 -10
  20. package/dist/components/CatalogPage/DefaultCatalogPage.esm.js.map +1 -1
  21. package/dist/components/CatalogTable/CatalogTable.esm.js +18 -9
  22. package/dist/components/CatalogTable/CatalogTable.esm.js.map +1 -1
  23. package/dist/components/CatalogTable/defaultCatalogTableColumnsFunc.esm.js +3 -2
  24. package/dist/components/CatalogTable/defaultCatalogTableColumnsFunc.esm.js.map +1 -1
  25. package/dist/components/RelatedEntitiesCard/RelatedEntitiesCard.esm.js +1 -0
  26. package/dist/components/RelatedEntitiesCard/RelatedEntitiesCard.esm.js.map +1 -1
  27. package/dist/index.d.ts +12 -1
  28. package/dist/package.json.esm.js +3 -1
  29. package/dist/package.json.esm.js.map +1 -1
  30. package/dist/types/DefaultCatalogPage.d-B0rL9YpH.d.ts +131 -0
  31. package/package.json +28 -26
  32. package/dist/types/DefaultCatalogPage.d-C6OI0JvL.d.ts +0 -47
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @backstage/plugin-catalog
2
2
 
3
+ ## 2.0.6-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - d8757b1: The entity list provider now fetches the entity list and the total count as two separate parallel requests when using cursor or offset pagination. The list query skips the expensive count computation (using `totalItems: 'exclude'`), so the table populates immediately. The count arrives asynchronously and updates the title. A new `totalItemsLoading` field is exposed on `EntityListContextProps` so consumers can distinguish a stale count from a fresh one.
8
+
9
+ The catalog table now keeps stale rows visible during filter changes and page navigation instead of replacing the entire table body with a spinner. The full-table spinner is only shown on the very first load when no data exists yet. The entity count in the title is dimmed while the count is refreshing, and a small spinner appears next to the title while rows are loading.
10
+
11
+ - 82cf16f: Added `CatalogExportButton`, which adds CSV and JSON export support to the `CatalogIndexPage`.
12
+ - a07e6a3: Added the correctly-spelled `RelatedEntitiesCard.domainEntityColumns` static property and deprecated the previous typoed `RelatedEntitiesCard.domainEntityColums` property. Existing references to the old property continue to work; switch to `domainEntityColumns` to avoid future removal.
13
+ - Updated dependencies
14
+ - @backstage/catalog-client@1.16.0-next.0
15
+ - @backstage/plugin-catalog-react@3.0.1-next.0
16
+ - @backstage/core-components@0.18.11-next.0
17
+ - @backstage/plugin-search-react@1.11.5-next.0
18
+ - @backstage/core-compat-api@0.5.12-next.0
19
+ - @backstage/integration-react@1.2.19-next.0
20
+ - @backstage/plugin-techdocs-react@1.3.12-next.0
21
+ - @backstage/plugin-scaffolder-common@2.2.1-next.0
22
+
3
23
  ## 2.0.5
4
24
 
5
25
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { convertLegacyRouteRef } from '@backstage/core-compat-api';
3
- import { PageBlueprint, coreExtensionData, createExtensionInput } from '@backstage/frontend-plugin-api';
3
+ import { createExtensionDataRef, createExtensionBlueprint, PageBlueprint, coreExtensionData, createExtensionInput } from '@backstage/frontend-plugin-api';
4
4
  import { z } from 'zod/v4';
5
5
  import { entityRouteRef, AsyncEntityProvider } from '@backstage/plugin-catalog-react';
6
6
  import { EntityContextMenuItemBlueprint, EntityHeaderBlueprint, defaultEntityContentGroupDefinitions, EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
@@ -9,9 +9,21 @@ import { rootRouteRef } from '../routes.esm.js';
9
9
  import { useEntityFromUrl } from '../components/CatalogEntityPage/useEntityFromUrl.esm.js';
10
10
  import { buildFilterFn } from './filter/FilterWrapper.esm.js';
11
11
 
12
+ const catalogExportConfigDataRef = createExtensionDataRef().with({
13
+ id: "catalog.export-customization"
14
+ });
15
+ const CatalogExportConfigBlueprint = createExtensionBlueprint({
16
+ kind: "catalog-export-config",
17
+ attachTo: { id: "page:catalog", input: "exportConfig" },
18
+ output: [catalogExportConfigDataRef],
19
+ factory(params) {
20
+ return [catalogExportConfigDataRef(params)];
21
+ }
22
+ });
12
23
  const catalogPage = PageBlueprint.makeWithOverrides({
13
24
  inputs: {
14
- filters: createExtensionInput([coreExtensionData.reactElement])
25
+ filters: createExtensionInput([coreExtensionData.reactElement]),
26
+ exportConfig: createExtensionInput([catalogExportConfigDataRef.optional()])
15
27
  },
16
28
  configSchema: {
17
29
  pagination: z.union([
@@ -21,7 +33,16 @@ const catalogPage = PageBlueprint.makeWithOverrides({
21
33
  limit: z.number().optional(),
22
34
  offset: z.number().optional()
23
35
  })
24
- ]).default(true)
36
+ ]).default(true),
37
+ exportSettings: z.object({
38
+ /** When true, displays the export button in the catalog interface. */
39
+ enabled: z.boolean().optional(),
40
+ /**
41
+ * When true, hides the built-in CSV and JSON export options.
42
+ * Useful when only custom exporters (provided via extensions) should be available.
43
+ */
44
+ disableBuiltinExporters: z.boolean().optional()
45
+ }).optional()
25
46
  },
26
47
  factory(originalFactory, { inputs, config }) {
27
48
  return originalFactory({
@@ -34,11 +55,35 @@ const catalogPage = PageBlueprint.makeWithOverrides({
34
55
  const filters = inputs.filters.map(
35
56
  (filter) => filter.get(coreExtensionData.reactElement)
36
57
  );
58
+ const mergedExportSettings = {
59
+ ...config.exportSettings
60
+ };
61
+ for (const exportConfigInput of inputs.exportConfig) {
62
+ const data = exportConfigInput.get(catalogExportConfigDataRef);
63
+ if (data) {
64
+ if (data.exporters) {
65
+ mergedExportSettings.exporters = {
66
+ ...mergedExportSettings.exporters,
67
+ ...data.exporters
68
+ };
69
+ }
70
+ if (data.columns && !mergedExportSettings.columns) {
71
+ mergedExportSettings.columns = data.columns;
72
+ }
73
+ if (data.onSuccess && !mergedExportSettings.onSuccess) {
74
+ mergedExportSettings.onSuccess = data.onSuccess;
75
+ }
76
+ if (data.onError && !mergedExportSettings.onError) {
77
+ mergedExportSettings.onError = data.onError;
78
+ }
79
+ }
80
+ }
37
81
  return /* @__PURE__ */ jsx(
38
82
  NfsDefaultCatalogPage,
39
83
  {
40
84
  filters: /* @__PURE__ */ jsx(Fragment, { children: filters }),
41
- pagination: config.pagination
85
+ pagination: config.pagination,
86
+ exportSettings: mergedExportSettings.enabled ? mergedExportSettings : void 0
42
87
  }
43
88
  );
44
89
  }
@@ -157,5 +202,5 @@ const catalogEntityPage = PageBlueprint.makeWithOverrides({
157
202
  });
158
203
  var pages = [catalogPage, catalogEntityPage];
159
204
 
160
- export { catalogEntityPage, catalogPage, pages as default };
205
+ export { CatalogExportConfigBlueprint, catalogEntityPage, catalogPage, pages as default };
161
206
  //# sourceMappingURL=pages.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pages.esm.js","sources":["../../src/alpha/pages.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\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 */\n\nimport { convertLegacyRouteRef } from '@backstage/core-compat-api';\nimport {\n coreExtensionData,\n createExtensionInput,\n PageBlueprint,\n} from '@backstage/frontend-plugin-api';\nimport { z } from 'zod/v4';\nimport {\n AsyncEntityProvider,\n entityRouteRef,\n} from '@backstage/plugin-catalog-react';\nimport {\n defaultEntityContentGroupDefinitions,\n EntityContentBlueprint,\n EntityContextMenuItemBlueprint,\n EntityHeaderBlueprint,\n EntityContentGroupDefinitions,\n} from '@backstage/plugin-catalog-react/alpha';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport { rootRouteRef } from '../routes';\nimport { useEntityFromUrl } from '../components/CatalogEntityPage/useEntityFromUrl';\nimport { buildFilterFn } from './filter/FilterWrapper';\n\nexport const catalogPage = PageBlueprint.makeWithOverrides({\n inputs: {\n filters: createExtensionInput([coreExtensionData.reactElement]),\n },\n configSchema: {\n pagination: z\n .union([\n z.boolean(),\n z.object({\n mode: z.enum(['cursor', 'offset']),\n limit: z.number().optional(),\n offset: z.number().optional(),\n }),\n ])\n .default(true),\n },\n factory(originalFactory, { inputs, config }) {\n return originalFactory({\n path: '/catalog',\n routeRef: rootRouteRef,\n icon: <CategoryIcon fontSize=\"inherit\" />,\n title: 'Catalog',\n loader: async () => {\n const { NfsDefaultCatalogPage } = await import(\n '../components/CatalogPage/DefaultCatalogPage'\n );\n const filters = inputs.filters.map(filter =>\n filter.get(coreExtensionData.reactElement),\n );\n return (\n <NfsDefaultCatalogPage\n filters={<>{filters}</>}\n pagination={config.pagination}\n />\n );\n },\n });\n },\n});\n\nexport const catalogEntityPage = PageBlueprint.makeWithOverrides({\n name: 'entity',\n inputs: {\n headers: createExtensionInput([\n EntityHeaderBlueprint.dataRefs.element.optional(),\n EntityHeaderBlueprint.dataRefs.filterFunction.optional(),\n ]),\n contents: createExtensionInput([\n coreExtensionData.reactElement,\n coreExtensionData.routePath,\n coreExtensionData.routeRef.optional(),\n EntityContentBlueprint.dataRefs.title,\n EntityContentBlueprint.dataRefs.filterFunction.optional(),\n EntityContentBlueprint.dataRefs.filterExpression.optional(),\n EntityContentBlueprint.dataRefs.group.optional(),\n EntityContentBlueprint.dataRefs.icon.optional(),\n ]),\n contextMenuItems: createExtensionInput([\n coreExtensionData.reactElement,\n EntityContextMenuItemBlueprint.dataRefs.filterFunction.optional(),\n ]),\n },\n configSchema: {\n groups: z\n .array(\n z.record(\n z.string(),\n z.object({\n title: z.string(),\n icon: z.string().optional(),\n aliases: z.array(z.string()).optional(),\n contentOrder: z.enum(['title', 'natural']).optional(),\n }),\n ),\n )\n .optional(),\n defaultContentOrder: z\n .enum(['title', 'natural'])\n .optional()\n .default('title'),\n showNavItemIcons: z.boolean().optional().default(false),\n },\n factory(originalFactory, { config, inputs }) {\n return originalFactory({\n path: '/catalog/:namespace/:kind/:name',\n noHeader: true,\n title: 'Catalog Entity',\n // NOTE: The `convertLegacyRouteRef` call here ensures that this route ref\n // is mutated to support the new frontend system. Removing this conversion\n // is a potentially breaking change since this is a singleton and the\n // route refs from `core-plugin-api` used to not support the new format.\n // This shouldn't be removed until we completely deprecate the\n // `core-compat-api` package.\n routeRef: convertLegacyRouteRef(entityRouteRef), // READ THE ABOVE\n loader: async () => {\n const { EntityLayout } = await import('./components/EntityLayout');\n\n const menuItems = inputs.contextMenuItems.map(item => ({\n element: item.get(coreExtensionData.reactElement),\n filter:\n item.get(EntityContextMenuItemBlueprint.dataRefs.filterFunction) ??\n (() => true),\n }));\n\n // Get available headers, sorted by if they have a filter function or not.\n // TODO(blam): we should really have priority or some specificity here which can be used to sort the headers.\n // That can be done with embedding the priority in the dataRef alongside the filter function.\n const headers = inputs.headers\n .map(header => ({\n element: header.get(EntityHeaderBlueprint.dataRefs.element),\n filter: header.get(EntityHeaderBlueprint.dataRefs.filterFunction),\n }))\n .sort((a, b) => {\n if (a.filter && !b.filter) return -1;\n if (!a.filter && b.filter) return 1;\n return 0;\n });\n\n const groupDefinitions =\n config.groups?.reduce(\n (rest, group) => ({ ...rest, ...group }),\n {} as EntityContentGroupDefinitions,\n ) ?? defaultEntityContentGroupDefinitions;\n\n const Component = () => {\n const entityFromUrl = useEntityFromUrl();\n const { entity } = entityFromUrl;\n const filteredMenuItems = entity\n ? menuItems.filter(i => i.filter(entity)).map(i => i.element)\n : [];\n\n const header = headers.find(\n h => !h.filter || h.filter(entity!),\n )?.element;\n\n return (\n <AsyncEntityProvider {...entityFromUrl}>\n <EntityLayout\n header={header}\n contextMenuItems={filteredMenuItems}\n groupDefinitions={groupDefinitions}\n defaultContentOrder={config.defaultContentOrder}\n showNavItemIcons={config.showNavItemIcons}\n >\n {inputs.contents.map(output => (\n <EntityLayout.Route\n group={output.get(EntityContentBlueprint.dataRefs.group)}\n key={output.get(coreExtensionData.routePath)}\n path={output.get(coreExtensionData.routePath)}\n title={output.get(EntityContentBlueprint.dataRefs.title)}\n icon={output.get(EntityContentBlueprint.dataRefs.icon)}\n if={buildFilterFn(\n output.get(\n EntityContentBlueprint.dataRefs.filterFunction,\n ),\n output.get(\n EntityContentBlueprint.dataRefs.filterExpression,\n ),\n )}\n >\n {output.get(coreExtensionData.reactElement)}\n </EntityLayout.Route>\n ))}\n </EntityLayout>\n </AsyncEntityProvider>\n );\n };\n\n return <Component />;\n },\n });\n },\n});\n\nexport default [catalogPage, catalogEntityPage];\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pages.esm.js","sources":["../../src/alpha/pages.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\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 */\n\nimport { convertLegacyRouteRef } from '@backstage/core-compat-api';\nimport {\n coreExtensionData,\n createExtensionInput,\n createExtensionDataRef,\n createExtensionBlueprint,\n PageBlueprint,\n} from '@backstage/frontend-plugin-api';\nimport { z } from 'zod/v4';\nimport {\n AsyncEntityProvider,\n entityRouteRef,\n} from '@backstage/plugin-catalog-react';\nimport {\n defaultEntityContentGroupDefinitions,\n EntityContentBlueprint,\n EntityContextMenuItemBlueprint,\n EntityHeaderBlueprint,\n EntityContentGroupDefinitions,\n} from '@backstage/plugin-catalog-react/alpha';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport { rootRouteRef } from '../routes';\nimport { useEntityFromUrl } from '../components/CatalogEntityPage/useEntityFromUrl';\nimport { buildFilterFn } from './filter/FilterWrapper';\nimport type { CatalogExportSettings } from '../components/CatalogExportButton';\n\nconst catalogExportConfigDataRef = createExtensionDataRef<{\n exporters?: CatalogExportSettings['exporters'];\n columns?: CatalogExportSettings['columns'];\n onSuccess?: CatalogExportSettings['onSuccess'];\n onError?: CatalogExportSettings['onError'];\n}>().with({\n id: 'catalog.export-customization',\n});\n\n/**\n * Blueprint for creating catalog export configuration extensions.\n * @public\n */\nexport const CatalogExportConfigBlueprint = createExtensionBlueprint({\n kind: 'catalog-export-config',\n attachTo: { id: 'page:catalog', input: 'exportConfig' },\n output: [catalogExportConfigDataRef],\n factory(params: {\n exporters?: CatalogExportSettings['exporters'];\n columns?: CatalogExportSettings['columns'];\n onSuccess?: CatalogExportSettings['onSuccess'];\n onError?: CatalogExportSettings['onError'];\n }) {\n return [catalogExportConfigDataRef(params)];\n },\n});\n\nexport const catalogPage = PageBlueprint.makeWithOverrides({\n inputs: {\n filters: createExtensionInput([coreExtensionData.reactElement]),\n exportConfig: createExtensionInput([catalogExportConfigDataRef.optional()]),\n },\n configSchema: {\n pagination: z\n .union([\n z.boolean(),\n z.object({\n mode: z.enum(['cursor', 'offset']),\n limit: z.number().optional(),\n offset: z.number().optional(),\n }),\n ])\n .default(true),\n exportSettings: z\n .object({\n /** When true, displays the export button in the catalog interface. */\n enabled: z.boolean().optional(),\n /**\n * When true, hides the built-in CSV and JSON export options.\n * Useful when only custom exporters (provided via extensions) should be available.\n */\n disableBuiltinExporters: z.boolean().optional(),\n })\n .optional(),\n },\n factory(originalFactory, { inputs, config }) {\n return originalFactory({\n path: '/catalog',\n routeRef: rootRouteRef,\n icon: <CategoryIcon fontSize=\"inherit\" />,\n title: 'Catalog',\n loader: async () => {\n const { NfsDefaultCatalogPage } = await import(\n '../components/CatalogPage/DefaultCatalogPage'\n );\n const filters = inputs.filters.map(filter =>\n filter.get(coreExtensionData.reactElement),\n );\n\n // Merge export customizers from all attached extensions\n const mergedExportSettings: CatalogExportSettings = {\n ...config.exportSettings,\n };\n\n for (const exportConfigInput of inputs.exportConfig) {\n const data = exportConfigInput.get(catalogExportConfigDataRef);\n if (data) {\n if (data.exporters) {\n mergedExportSettings.exporters = {\n ...mergedExportSettings.exporters,\n ...data.exporters,\n };\n }\n if (data.columns && !mergedExportSettings.columns) {\n mergedExportSettings.columns = data.columns;\n }\n if (data.onSuccess && !mergedExportSettings.onSuccess) {\n mergedExportSettings.onSuccess = data.onSuccess;\n }\n if (data.onError && !mergedExportSettings.onError) {\n mergedExportSettings.onError = data.onError;\n }\n }\n }\n\n return (\n <NfsDefaultCatalogPage\n filters={<>{filters}</>}\n pagination={config.pagination}\n exportSettings={\n mergedExportSettings.enabled ? mergedExportSettings : undefined\n }\n />\n );\n },\n });\n },\n});\n\nexport const catalogEntityPage = PageBlueprint.makeWithOverrides({\n name: 'entity',\n inputs: {\n headers: createExtensionInput([\n EntityHeaderBlueprint.dataRefs.element.optional(),\n EntityHeaderBlueprint.dataRefs.filterFunction.optional(),\n ]),\n contents: createExtensionInput([\n coreExtensionData.reactElement,\n coreExtensionData.routePath,\n coreExtensionData.routeRef.optional(),\n EntityContentBlueprint.dataRefs.title,\n EntityContentBlueprint.dataRefs.filterFunction.optional(),\n EntityContentBlueprint.dataRefs.filterExpression.optional(),\n EntityContentBlueprint.dataRefs.group.optional(),\n EntityContentBlueprint.dataRefs.icon.optional(),\n ]),\n contextMenuItems: createExtensionInput([\n coreExtensionData.reactElement,\n EntityContextMenuItemBlueprint.dataRefs.filterFunction.optional(),\n ]),\n },\n configSchema: {\n groups: z\n .array(\n z.record(\n z.string(),\n z.object({\n title: z.string(),\n icon: z.string().optional(),\n aliases: z.array(z.string()).optional(),\n contentOrder: z.enum(['title', 'natural']).optional(),\n }),\n ),\n )\n .optional(),\n defaultContentOrder: z\n .enum(['title', 'natural'])\n .optional()\n .default('title'),\n showNavItemIcons: z.boolean().optional().default(false),\n },\n factory(originalFactory, { config, inputs }) {\n return originalFactory({\n path: '/catalog/:namespace/:kind/:name',\n noHeader: true,\n title: 'Catalog Entity',\n // NOTE: The `convertLegacyRouteRef` call here ensures that this route ref\n // is mutated to support the new frontend system. Removing this conversion\n // is a potentially breaking change since this is a singleton and the\n // route refs from `core-plugin-api` used to not support the new format.\n // This shouldn't be removed until we completely deprecate the\n // `core-compat-api` package.\n routeRef: convertLegacyRouteRef(entityRouteRef), // READ THE ABOVE\n loader: async () => {\n const { EntityLayout } = await import('./components/EntityLayout');\n\n const menuItems = inputs.contextMenuItems.map(item => ({\n element: item.get(coreExtensionData.reactElement),\n filter:\n item.get(EntityContextMenuItemBlueprint.dataRefs.filterFunction) ??\n (() => true),\n }));\n\n // Get available headers, sorted by if they have a filter function or not.\n // TODO(blam): we should really have priority or some specificity here which can be used to sort the headers.\n // That can be done with embedding the priority in the dataRef alongside the filter function.\n const headers = inputs.headers\n .map(header => ({\n element: header.get(EntityHeaderBlueprint.dataRefs.element),\n filter: header.get(EntityHeaderBlueprint.dataRefs.filterFunction),\n }))\n .sort((a, b) => {\n if (a.filter && !b.filter) return -1;\n if (!a.filter && b.filter) return 1;\n return 0;\n });\n\n const groupDefinitions =\n config.groups?.reduce(\n (rest, group) => ({ ...rest, ...group }),\n {} as EntityContentGroupDefinitions,\n ) ?? defaultEntityContentGroupDefinitions;\n\n const Component = () => {\n const entityFromUrl = useEntityFromUrl();\n const { entity } = entityFromUrl;\n const filteredMenuItems = entity\n ? menuItems.filter(i => i.filter(entity)).map(i => i.element)\n : [];\n\n const header = headers.find(\n h => !h.filter || h.filter(entity!),\n )?.element;\n\n return (\n <AsyncEntityProvider {...entityFromUrl}>\n <EntityLayout\n header={header}\n contextMenuItems={filteredMenuItems}\n groupDefinitions={groupDefinitions}\n defaultContentOrder={config.defaultContentOrder}\n showNavItemIcons={config.showNavItemIcons}\n >\n {inputs.contents.map(output => (\n <EntityLayout.Route\n group={output.get(EntityContentBlueprint.dataRefs.group)}\n key={output.get(coreExtensionData.routePath)}\n path={output.get(coreExtensionData.routePath)}\n title={output.get(EntityContentBlueprint.dataRefs.title)}\n icon={output.get(EntityContentBlueprint.dataRefs.icon)}\n if={buildFilterFn(\n output.get(\n EntityContentBlueprint.dataRefs.filterFunction,\n ),\n output.get(\n EntityContentBlueprint.dataRefs.filterExpression,\n ),\n )}\n >\n {output.get(coreExtensionData.reactElement)}\n </EntityLayout.Route>\n ))}\n </EntityLayout>\n </AsyncEntityProvider>\n );\n };\n\n return <Component />;\n },\n });\n },\n});\n\nexport default [catalogPage, catalogEntityPage];\n"],"names":[],"mappings":"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"}
@@ -127,6 +127,17 @@ const catalogTranslationRef = createTranslationRef({
127
127
  description: "This entity is not referenced by any location and is therefore not receiving updates.",
128
128
  actionButtonTitle: "Delete entity"
129
129
  },
130
+ catalogExportButton: {
131
+ triggerButtonTitle: "Export selection",
132
+ dialogTitle: "Export catalog selection",
133
+ formatLabel: "Format",
134
+ columnsLabel: "Columns",
135
+ cancelButtonTitle: "Cancel",
136
+ confirmButtonTitle: "Confirm",
137
+ exportingButtonTitle: "Exporting\u2026",
138
+ successMessage: "Catalog exported successfully",
139
+ errorMessage: "Failed to export catalog: {{errorMessage}}"
140
+ },
130
141
  entityProcessingErrorsDescription: "The error below originates from",
131
142
  entityRelationWarningDescription: "This entity has relations to other entities, which can't be found in the catalog.\n Entities not found are: ",
132
143
  hasComponentsCard: {
@@ -1 +1 @@
1
- {"version":3,"file":"translation.esm.js","sources":["../../src/alpha/translation.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\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 */\n\nimport { createTranslationRef } from '@backstage/core-plugin-api/alpha';\n\n/** @public */\nexport const catalogTranslationRef = createTranslationRef({\n id: 'catalog',\n messages: {\n indexPage: {\n title: `{{orgName}} Catalog`,\n createButtonTitle: 'Create',\n supportButtonContent: 'All your software catalog entities',\n },\n entityPage: {\n notFoundMessage: 'There is no {{kind}} with the requested {{link}}.',\n notFoundLinkText: 'kind, namespace, and name',\n },\n aboutCard: {\n title: 'About',\n refreshButtonTitle: 'Schedule entity refresh',\n editButtonTitle: 'Edit Metadata',\n editButtonAriaLabel: 'Edit',\n createSimilarButtonTitle: 'Create something similar',\n refreshScheduledMessage: 'Refresh scheduled',\n refreshButtonAriaLabel: 'Refresh',\n launchTemplate: 'Launch Template',\n viewTechdocs: 'View TechDocs',\n viewSource: 'View Source',\n unknown: 'unknown',\n descriptionField: {\n label: 'Description',\n value: 'No description',\n },\n ownerField: {\n label: 'Owner',\n value: 'No Owner',\n },\n domainField: {\n label: 'Domain',\n value: 'No Domain',\n },\n systemField: {\n label: 'System',\n value: 'No System',\n },\n parentComponentField: {\n label: 'Parent Component',\n value: 'No Parent Component',\n },\n kindField: {\n label: 'Kind',\n },\n typeField: {\n label: 'Type',\n },\n lifecycleField: {\n label: 'Lifecycle',\n },\n tagsField: {\n label: 'Tags',\n value: 'No Tags',\n },\n targetsField: {\n label: 'Targets',\n },\n },\n searchResultItem: {\n kind: 'Kind',\n type: 'Type',\n lifecycle: 'Lifecycle',\n owner: 'Owner',\n },\n catalogTable: {\n warningPanelTitle: 'Could not fetch catalog entities.',\n viewActionTitle: 'View',\n editActionTitle: 'Edit',\n starActionTitle: 'Add to favorites',\n unStarActionTitle: 'Remove from favorites',\n allFilters: 'All',\n },\n dependencyOfComponentsCard: {\n title: 'Dependency of components',\n emptyMessage: 'No component depends on this component.',\n },\n dependsOnComponentsCard: {\n title: 'Depends on components',\n emptyMessage: 'No component is a dependency of this component.',\n },\n dependsOnResourcesCard: {\n title: 'Depends on resources',\n emptyMessage: 'No resource is a dependency of this component.',\n },\n entityContextMenu: {\n copiedMessage: 'Copied!',\n moreButtonTitle: 'More',\n inspectMenuTitle: 'Inspect entity',\n copyURLMenuTitle: 'Copy entity URL',\n unregisterMenuTitle: 'Unregister entity',\n moreButtonAriaLabel: 'more',\n },\n entityLabelsCard: {\n title: 'Labels',\n columnKeyLabel: 'Label',\n columnValueLabel: 'Value',\n emptyDescription:\n 'No labels defined for this entity. You can add labels to your entity YAML as shown in the highlighted example below:',\n readMoreButtonTitle: 'Read more',\n },\n entityLabels: {\n warningPanelTitle: 'Entity not found',\n ownerLabel: 'Owner',\n lifecycleLabel: 'Lifecycle',\n },\n entityLinksCard: {\n title: 'Links',\n emptyDescription:\n 'No links defined for this entity. You can add links to your entity YAML as shown in the highlighted example below:',\n readMoreButtonTitle: 'Read more',\n },\n entityNotFound: {\n title: 'Entity was not found',\n description:\n 'Want to help us build this? Check out our Getting Started documentation.',\n docButtonTitle: 'DOCS',\n },\n entityTabs: {\n tabsAriaLabel: 'Tabs',\n },\n deleteEntity: {\n dialogTitle: 'Are you sure you want to delete this entity?',\n deleteButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n description:\n 'This entity is not referenced by any location and is therefore not receiving updates.',\n actionButtonTitle: 'Delete entity',\n },\n entityProcessingErrorsDescription: 'The error below originates from',\n entityRelationWarningDescription:\n \"This entity has relations to other entities, which can't be found in the catalog.\\n Entities not found are: \",\n hasComponentsCard: {\n title: 'Has components',\n emptyMessage: 'No component is part of this system.',\n },\n hasResourcesCard: {\n title: 'Has resources',\n emptyMessage: 'No resource is part of this system.',\n },\n hasSubcomponentsCard: {\n title: 'Has subcomponents',\n emptyMessage: 'No subcomponent is part of this component.',\n },\n hasSubdomainsCard: {\n title: 'Has subdomains',\n emptyMessage: 'No subdomain is part of this domain.',\n },\n hasSystemsCard: {\n title: 'Has systems',\n emptyMessage: 'No system is part of this domain.',\n },\n relatedEntitiesCard: {\n emptyHelpLinkTitle: 'Learn how to change this.',\n },\n systemDiagramCard: {\n title: 'System Diagram',\n description: 'Use pinch & zoom to move around the diagram.',\n edgeLabels: {\n partOf: 'part of',\n provides: 'provides',\n dependsOn: 'depends on',\n },\n },\n },\n});\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"translation.esm.js","sources":["../../src/alpha/translation.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\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 */\n\nimport { createTranslationRef } from '@backstage/core-plugin-api/alpha';\n\n/** @public */\nexport const catalogTranslationRef = createTranslationRef({\n id: 'catalog',\n messages: {\n indexPage: {\n title: `{{orgName}} Catalog`,\n createButtonTitle: 'Create',\n supportButtonContent: 'All your software catalog entities',\n },\n entityPage: {\n notFoundMessage: 'There is no {{kind}} with the requested {{link}}.',\n notFoundLinkText: 'kind, namespace, and name',\n },\n aboutCard: {\n title: 'About',\n refreshButtonTitle: 'Schedule entity refresh',\n editButtonTitle: 'Edit Metadata',\n editButtonAriaLabel: 'Edit',\n createSimilarButtonTitle: 'Create something similar',\n refreshScheduledMessage: 'Refresh scheduled',\n refreshButtonAriaLabel: 'Refresh',\n launchTemplate: 'Launch Template',\n viewTechdocs: 'View TechDocs',\n viewSource: 'View Source',\n unknown: 'unknown',\n descriptionField: {\n label: 'Description',\n value: 'No description',\n },\n ownerField: {\n label: 'Owner',\n value: 'No Owner',\n },\n domainField: {\n label: 'Domain',\n value: 'No Domain',\n },\n systemField: {\n label: 'System',\n value: 'No System',\n },\n parentComponentField: {\n label: 'Parent Component',\n value: 'No Parent Component',\n },\n kindField: {\n label: 'Kind',\n },\n typeField: {\n label: 'Type',\n },\n lifecycleField: {\n label: 'Lifecycle',\n },\n tagsField: {\n label: 'Tags',\n value: 'No Tags',\n },\n targetsField: {\n label: 'Targets',\n },\n },\n searchResultItem: {\n kind: 'Kind',\n type: 'Type',\n lifecycle: 'Lifecycle',\n owner: 'Owner',\n },\n catalogTable: {\n warningPanelTitle: 'Could not fetch catalog entities.',\n viewActionTitle: 'View',\n editActionTitle: 'Edit',\n starActionTitle: 'Add to favorites',\n unStarActionTitle: 'Remove from favorites',\n allFilters: 'All',\n },\n dependencyOfComponentsCard: {\n title: 'Dependency of components',\n emptyMessage: 'No component depends on this component.',\n },\n dependsOnComponentsCard: {\n title: 'Depends on components',\n emptyMessage: 'No component is a dependency of this component.',\n },\n dependsOnResourcesCard: {\n title: 'Depends on resources',\n emptyMessage: 'No resource is a dependency of this component.',\n },\n entityContextMenu: {\n copiedMessage: 'Copied!',\n moreButtonTitle: 'More',\n inspectMenuTitle: 'Inspect entity',\n copyURLMenuTitle: 'Copy entity URL',\n unregisterMenuTitle: 'Unregister entity',\n moreButtonAriaLabel: 'more',\n },\n entityLabelsCard: {\n title: 'Labels',\n columnKeyLabel: 'Label',\n columnValueLabel: 'Value',\n emptyDescription:\n 'No labels defined for this entity. You can add labels to your entity YAML as shown in the highlighted example below:',\n readMoreButtonTitle: 'Read more',\n },\n entityLabels: {\n warningPanelTitle: 'Entity not found',\n ownerLabel: 'Owner',\n lifecycleLabel: 'Lifecycle',\n },\n entityLinksCard: {\n title: 'Links',\n emptyDescription:\n 'No links defined for this entity. You can add links to your entity YAML as shown in the highlighted example below:',\n readMoreButtonTitle: 'Read more',\n },\n entityNotFound: {\n title: 'Entity was not found',\n description:\n 'Want to help us build this? Check out our Getting Started documentation.',\n docButtonTitle: 'DOCS',\n },\n entityTabs: {\n tabsAriaLabel: 'Tabs',\n },\n deleteEntity: {\n dialogTitle: 'Are you sure you want to delete this entity?',\n deleteButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n description:\n 'This entity is not referenced by any location and is therefore not receiving updates.',\n actionButtonTitle: 'Delete entity',\n },\n catalogExportButton: {\n triggerButtonTitle: 'Export selection',\n dialogTitle: 'Export catalog selection',\n formatLabel: 'Format',\n columnsLabel: 'Columns',\n cancelButtonTitle: 'Cancel',\n confirmButtonTitle: 'Confirm',\n exportingButtonTitle: 'Exporting…',\n successMessage: 'Catalog exported successfully',\n errorMessage: 'Failed to export catalog: {{errorMessage}}',\n },\n entityProcessingErrorsDescription: 'The error below originates from',\n entityRelationWarningDescription:\n \"This entity has relations to other entities, which can't be found in the catalog.\\n Entities not found are: \",\n hasComponentsCard: {\n title: 'Has components',\n emptyMessage: 'No component is part of this system.',\n },\n hasResourcesCard: {\n title: 'Has resources',\n emptyMessage: 'No resource is part of this system.',\n },\n hasSubcomponentsCard: {\n title: 'Has subcomponents',\n emptyMessage: 'No subcomponent is part of this component.',\n },\n hasSubdomainsCard: {\n title: 'Has subdomains',\n emptyMessage: 'No subdomain is part of this domain.',\n },\n hasSystemsCard: {\n title: 'Has systems',\n emptyMessage: 'No system is part of this domain.',\n },\n relatedEntitiesCard: {\n emptyHelpLinkTitle: 'Learn how to change this.',\n },\n systemDiagramCard: {\n title: 'System Diagram',\n description: 'Use pinch & zoom to move around the diagram.',\n edgeLabels: {\n partOf: 'part of',\n provides: 'provides',\n dependsOn: 'depends on',\n },\n },\n },\n});\n"],"names":[],"mappings":"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"}
package/dist/alpha.d.ts CHANGED
@@ -6,9 +6,11 @@ import * as _backstage_catalog_model from '@backstage/catalog-model';
6
6
  import * as _backstage_filter_predicates from '@backstage/filter-predicates';
7
7
  import * as react from 'react';
8
8
  import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
9
- export { N as CatalogIndexPage, D as CatalogIndexPageProps, a as CatalogTableColumnsFunc, C as CatalogTableRow } from './types/DefaultCatalogPage.d-C6OI0JvL.js';
9
+ import { b as CatalogExportSettings } from './types/DefaultCatalogPage.d-B0rL9YpH.js';
10
+ export { c as CatalogExportSettingsColumn, d as CatalogExporter, e as CatalogExporterConfig, N as CatalogIndexPage, D as CatalogIndexPageProps, a as CatalogTableColumnsFunc, C as CatalogTableRow } from './types/DefaultCatalogPage.d-B0rL9YpH.js';
10
11
  import 'react/jsx-runtime';
11
12
  import '@backstage/plugin-catalog-react';
13
+ import '@backstage/catalog-client';
12
14
 
13
15
  /** @alpha */
14
16
  declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin<{
@@ -557,6 +559,10 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
557
559
  limit?: number | undefined;
558
560
  offset?: number | undefined;
559
561
  };
562
+ exportSettings: {
563
+ enabled?: boolean | undefined;
564
+ disableBuiltinExporters?: boolean | undefined;
565
+ } | undefined;
560
566
  path: string | undefined;
561
567
  title: string | undefined;
562
568
  };
@@ -566,6 +572,10 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
566
572
  limit?: number | undefined;
567
573
  offset?: number | undefined;
568
574
  } | undefined;
575
+ exportSettings?: {
576
+ enabled?: boolean | undefined;
577
+ disableBuiltinExporters?: boolean | undefined;
578
+ } | undefined;
569
579
  path?: string | undefined;
570
580
  title?: string | undefined;
571
581
  };
@@ -593,6 +603,18 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
593
603
  optional: false;
594
604
  internal: false;
595
605
  }>;
606
+ exportConfig: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<{
607
+ exporters?: CatalogExportSettings["exporters"];
608
+ columns?: CatalogExportSettings["columns"];
609
+ onSuccess?: CatalogExportSettings["onSuccess"];
610
+ onError?: CatalogExportSettings["onError"];
611
+ }, "catalog.export-customization", {
612
+ optional: true;
613
+ }>, {
614
+ singleton: false;
615
+ optional: false;
616
+ internal: false;
617
+ }>;
596
618
  };
597
619
  kind: "page";
598
620
  name: undefined;
@@ -711,6 +733,30 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
711
733
  }>;
712
734
  }>;
713
735
 
736
+ /**
737
+ * Blueprint for creating catalog export configuration extensions.
738
+ * @public
739
+ */
740
+ declare const CatalogExportConfigBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
741
+ kind: "catalog-export-config";
742
+ params: {
743
+ exporters?: CatalogExportSettings["exporters"];
744
+ columns?: CatalogExportSettings["columns"];
745
+ onSuccess?: CatalogExportSettings["onSuccess"];
746
+ onError?: CatalogExportSettings["onError"];
747
+ };
748
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<{
749
+ exporters?: CatalogExportSettings["exporters"];
750
+ columns?: CatalogExportSettings["columns"];
751
+ onSuccess?: CatalogExportSettings["onSuccess"];
752
+ onError?: CatalogExportSettings["onError"];
753
+ }, "catalog.export-customization", {}>;
754
+ inputs: {};
755
+ config: {};
756
+ configInput: {};
757
+ dataRefs: never;
758
+ }>;
759
+
714
760
  /**
715
761
  * @alpha
716
762
  * @deprecated Import from `@backstage/plugin-catalog` instead.
@@ -790,6 +836,15 @@ declare const catalogTranslationRef: _backstage_frontend_plugin_api.TranslationR
790
836
  readonly "entityNotFound.description": "Want to help us build this? Check out our Getting Started documentation.";
791
837
  readonly "entityNotFound.docButtonTitle": "DOCS";
792
838
  readonly "entityTabs.tabsAriaLabel": "Tabs";
839
+ readonly "catalogExportButton.errorMessage": "Failed to export catalog: {{errorMessage}}";
840
+ readonly "catalogExportButton.cancelButtonTitle": "Cancel";
841
+ readonly "catalogExportButton.dialogTitle": "Export catalog selection";
842
+ readonly "catalogExportButton.triggerButtonTitle": "Export selection";
843
+ readonly "catalogExportButton.formatLabel": "Format";
844
+ readonly "catalogExportButton.columnsLabel": "Columns";
845
+ readonly "catalogExportButton.confirmButtonTitle": "Confirm";
846
+ readonly "catalogExportButton.exportingButtonTitle": "Exporting…";
847
+ readonly "catalogExportButton.successMessage": "Catalog exported successfully";
793
848
  readonly entityProcessingErrorsDescription: "The error below originates from";
794
849
  readonly entityRelationWarningDescription: "This entity has relations to other entities, which can't be found in the catalog.\n Entities not found are: ";
795
850
  readonly "hasComponentsCard.title": "Has components";
@@ -810,4 +865,4 @@ declare const catalogTranslationRef: _backstage_frontend_plugin_api.TranslationR
810
865
  readonly "systemDiagramCard.edgeLabels.provides": "provides";
811
866
  }>;
812
867
 
813
- export { catalogTranslationRef, _default as default };
868
+ export { CatalogExportConfigBlueprint, CatalogExportSettings, catalogTranslationRef, _default as default };
package/dist/alpha.esm.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { default } from './alpha/plugin.esm.js';
2
+ export { CatalogExportConfigBlueprint } from './alpha/pages.esm.js';
2
3
  import 'react/jsx-runtime';
3
4
  import 'react-router-dom';
4
5
  export { NfsDefaultCatalogPage as CatalogIndexPage } from './components/CatalogPage/DefaultCatalogPage.esm.js';
@@ -1 +1 @@
1
- {"version":3,"file":"alpha.esm.js","sources":["../src/alpha/index.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\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 */\n\nexport { default } from './plugin';\n\nexport { NfsDefaultCatalogPage as CatalogIndexPage } from '../components/CatalogPage';\nexport type { DefaultCatalogPageProps as CatalogIndexPageProps } from '../components/CatalogPage';\nexport type {\n CatalogTableRow,\n CatalogTableColumnsFunc,\n} from '../components/CatalogTable';\n\nimport { catalogTranslationRef as _catalogTranslationRef } from './translation';\n\n/**\n * @alpha\n * @deprecated Import from `@backstage/plugin-catalog` instead.\n */\nexport const catalogTranslationRef = _catalogTranslationRef;\n"],"names":["_catalogTranslationRef"],"mappings":";;;;;;AA+BO,MAAM,qBAAA,GAAwBA;;;;"}
1
+ {"version":3,"file":"alpha.esm.js","sources":["../src/alpha/index.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\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 */\n\nexport { default } from './plugin';\n\nexport { CatalogExportConfigBlueprint } from './pages';\nexport type {\n CatalogExportSettings,\n CatalogExporterConfig,\n CatalogExporter,\n CatalogExportSettingsColumn,\n} from '../components/CatalogExportButton';\nexport { NfsDefaultCatalogPage as CatalogIndexPage } from '../components/CatalogPage';\nexport type { DefaultCatalogPageProps as CatalogIndexPageProps } from '../components/CatalogPage';\nexport type {\n CatalogTableRow,\n CatalogTableColumnsFunc,\n} from '../components/CatalogTable';\n\nimport { catalogTranslationRef as _catalogTranslationRef } from './translation';\n\n/**\n * @alpha\n * @deprecated Import from `@backstage/plugin-catalog` instead.\n */\nexport const catalogTranslationRef = _catalogTranslationRef;\n"],"names":["_catalogTranslationRef"],"mappings":";;;;;;;AAsCO,MAAM,qBAAA,GAAwBA;;;;"}
@@ -0,0 +1,163 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useState, useEffect, useCallback } from 'react';
3
+ import { RiDownloadLine } from '@remixicon/react';
4
+ import { useApiHolder } from '@backstage/core-plugin-api';
5
+ import { useTranslationRef, toastApiRef } from '@backstage/frontend-plugin-api';
6
+ import { catalogTranslationRef } from '../../alpha/translation.esm.js';
7
+ import { Button, Dialog, DialogHeader, DialogBody, Flex, Select, Text, Checkbox, DialogFooter } from '@backstage/ui';
8
+ import { useStreamingExport } from './file-download/useStreamingExport.esm.js';
9
+ import { getColumnTitle } from './file-download/serializeEntities.esm.js';
10
+
11
+ var CatalogExportType = /* @__PURE__ */ ((CatalogExportType2) => {
12
+ CatalogExportType2["CSV"] = "csv";
13
+ CatalogExportType2["JSON"] = "json";
14
+ return CatalogExportType2;
15
+ })(CatalogExportType || {});
16
+ const DEFAULT_EXPORT_COLUMNS = [
17
+ { entityFilterKey: "metadata.name", title: "Name" },
18
+ { entityFilterKey: "spec.type", title: "Type" },
19
+ { entityFilterKey: "spec.owner", title: "Owner" },
20
+ { entityFilterKey: "metadata.description", title: "Description" }
21
+ ];
22
+ const CatalogExportButton = ({
23
+ settings
24
+ }) => {
25
+ const { t } = useTranslationRef(catalogTranslationRef);
26
+ const { exportStream, loading, error } = useStreamingExport();
27
+ const [open, setOpen] = useState(false);
28
+ const exporters = settings?.exporters;
29
+ const disableBuiltinExporters = settings?.disableBuiltinExporters;
30
+ const onSuccess = settings?.onSuccess;
31
+ const onError = settings?.onError;
32
+ const apis = useApiHolder();
33
+ const toastApi = apis.get(toastApiRef);
34
+ const [isExporting, setIsExporting] = useState(false);
35
+ const effectiveColumns = settings?.columns ?? DEFAULT_EXPORT_COLUMNS;
36
+ const allExportOptions = [
37
+ ...disableBuiltinExporters ? [] : Object.values(CatalogExportType).map((format) => ({
38
+ value: format,
39
+ label: format.toUpperCase()
40
+ })),
41
+ ...Object.entries(exporters ?? {}).map(([key, exporter]) => ({
42
+ value: key,
43
+ label: exporter.label ?? key.toUpperCase()
44
+ }))
45
+ ];
46
+ const [exportFormat, setExportFormat] = useState(
47
+ allExportOptions[0]?.value ?? ""
48
+ );
49
+ const [selectedColumnTitles, setSelectedColumnTitles] = useState(
50
+ () => new Set(effectiveColumns.map((c) => getColumnTitle(c)))
51
+ );
52
+ const selectedColumns = effectiveColumns.filter(
53
+ (c) => selectedColumnTitles.has(getColumnTitle(c))
54
+ );
55
+ const handleOpenDialog = () => {
56
+ setSelectedColumnTitles(
57
+ new Set(effectiveColumns.map((c) => getColumnTitle(c)))
58
+ );
59
+ setOpen(true);
60
+ };
61
+ const toggleColumn = (title) => {
62
+ setSelectedColumnTitles((prev) => {
63
+ const next = new Set(prev);
64
+ if (next.has(title)) {
65
+ next.delete(title);
66
+ } else {
67
+ next.add(title);
68
+ }
69
+ return next;
70
+ });
71
+ };
72
+ useEffect(() => {
73
+ if (isExporting && !loading) {
74
+ if (error) {
75
+ if (onError) {
76
+ onError({ error });
77
+ } else {
78
+ toastApi.post({
79
+ title: t("catalogExportButton.errorMessage", {
80
+ errorMessage: error.message
81
+ }),
82
+ status: "danger"
83
+ });
84
+ }
85
+ } else {
86
+ if (onSuccess) {
87
+ onSuccess();
88
+ } else {
89
+ toastApi.post({
90
+ title: t("catalogExportButton.successMessage"),
91
+ status: "success"
92
+ });
93
+ }
94
+ }
95
+ setOpen(false);
96
+ setIsExporting(false);
97
+ }
98
+ }, [isExporting, loading, error, toastApi, onSuccess, onError, t]);
99
+ const handleExport = useCallback(async () => {
100
+ setIsExporting(true);
101
+ const exporterFn = exporters?.[exportFormat];
102
+ await exportStream({
103
+ exportFormat,
104
+ filename: `catalog-export.${exportFormat}`,
105
+ columns: selectedColumns,
106
+ exporter: exporterFn?.exporter
107
+ });
108
+ }, [exportFormat, exportStream, selectedColumns, exporters]);
109
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
110
+ /* @__PURE__ */ jsx(
111
+ Button,
112
+ {
113
+ variant: "secondary",
114
+ iconStart: /* @__PURE__ */ jsx(RiDownloadLine, {}),
115
+ onPress: handleOpenDialog,
116
+ children: t("catalogExportButton.triggerButtonTitle")
117
+ }
118
+ ),
119
+ /* @__PURE__ */ jsxs(Dialog, { isOpen: open, onOpenChange: (isOpen) => !isOpen && setOpen(false), children: [
120
+ /* @__PURE__ */ jsx(DialogHeader, { children: t("catalogExportButton.dialogTitle") }),
121
+ /* @__PURE__ */ jsx(DialogBody, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "4", children: [
122
+ /* @__PURE__ */ jsx(
123
+ Select,
124
+ {
125
+ "data-testid": "format-select",
126
+ label: t("catalogExportButton.formatLabel"),
127
+ options: allExportOptions,
128
+ value: exportFormat,
129
+ onChange: (key) => setExportFormat(key)
130
+ }
131
+ ),
132
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "1", children: [
133
+ /* @__PURE__ */ jsx(Text, { variant: "body-small", weight: "bold", children: t("catalogExportButton.columnsLabel") }),
134
+ effectiveColumns.map((col) => /* @__PURE__ */ jsx(
135
+ Checkbox,
136
+ {
137
+ isSelected: selectedColumnTitles.has(getColumnTitle(col)),
138
+ onChange: () => toggleColumn(getColumnTitle(col)),
139
+ children: getColumnTitle(col)
140
+ },
141
+ col.entityFilterKey
142
+ ))
143
+ ] })
144
+ ] }) }),
145
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
146
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onPress: () => setOpen(false), children: t("catalogExportButton.cancelButtonTitle") }),
147
+ /* @__PURE__ */ jsx(
148
+ Button,
149
+ {
150
+ variant: "primary",
151
+ onPress: () => handleExport(),
152
+ isDisabled: !exportFormat || loading || selectedColumns.length === 0,
153
+ loading,
154
+ children: loading ? t("catalogExportButton.exportingButtonTitle") : t("catalogExportButton.confirmButtonTitle")
155
+ }
156
+ )
157
+ ] })
158
+ ] })
159
+ ] });
160
+ };
161
+
162
+ export { CatalogExportButton, CatalogExportType };
163
+ //# sourceMappingURL=CatalogExportButton.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CatalogExportButton.esm.js","sources":["../../../src/components/CatalogExportButton/CatalogExportButton.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\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 { useCallback, useEffect, useState } from 'react';\nimport { RiDownloadLine } from '@remixicon/react';\nimport { useApiHolder } from '@backstage/core-plugin-api';\nimport { useTranslationRef, toastApiRef } from '@backstage/frontend-plugin-api';\nimport { catalogTranslationRef } from '../../alpha/translation';\nimport {\n Button,\n Checkbox,\n Dialog,\n DialogBody,\n DialogFooter,\n DialogHeader,\n Flex,\n Select,\n Text,\n} from '@backstage/ui';\nimport { useStreamingExport } from './file-download';\nimport type { CatalogExportSettingsColumn } from './file-download/serializeEntities';\nimport { getColumnTitle } from './file-download/serializeEntities';\nimport type { CatalogExporter } from './file-download/useStreamingExport';\n\n/**\n * Custom exporter configuration for a catalog export format.\n * @public\n */\nexport interface CatalogExporterConfig {\n /** The exporter function that generates the export content. */\n exporter: CatalogExporter;\n /** Optional display label shown in the format selector. Defaults to the format key in uppercase. */\n label?: string;\n}\n\n/**\n * Settings for configuring the catalog export functionality.\n *\n * @public\n */\nexport interface CatalogExportSettings {\n /**\n * When true, displays the export button in the catalog interface.\n * Defaults to false if not specified.\n */\n enabled?: boolean;\n\n /**\n * Custom columns to include in the export.\n * Each column specifies an entity field path and a display title.\n * If not specified, uses default columns: Name, Type, Owner, Description.\n */\n columns?: CatalogExportSettingsColumn[];\n\n /**\n * Map of custom export format handlers beyond the built-in CSV and JSON formats.\n * Key is the format name (e.g., 'xml', 'yaml'), value is the exporter configuration.\n * Custom formats will appear as options in the export dialog.\n */\n exporters?: Record<string, CatalogExporterConfig>;\n\n /**\n * When true, hides the built-in CSV and JSON export options from the dialog.\n * Useful when only custom exporters should be available.\n */\n disableBuiltinExporters?: boolean;\n\n /**\n * Callback function invoked when the export completes successfully.\n * Useful for showing notifications or performing post-export actions.\n */\n onSuccess?: () => void;\n\n /**\n * Callback function invoked when the export fails.\n * Receives an object containing the error for custom error handling.\n */\n onError?: (options: { error: Error }) => void;\n}\n\n/**\n * The available export formats for the catalog export.\n * Currently supports CSV and JSON.\n *\n * @public\n */\nexport enum CatalogExportType {\n CSV = 'csv',\n JSON = 'json',\n}\n\n/**\n * The available default export columns for the catalog export.\n * These can be overridden by providing custom columns in the export button options.\n *\n * @private\n */\nconst DEFAULT_EXPORT_COLUMNS = [\n { entityFilterKey: 'metadata.name', title: 'Name' },\n { entityFilterKey: 'spec.type', title: 'Type' },\n { entityFilterKey: 'spec.owner', title: 'Owner' },\n { entityFilterKey: 'metadata.description', title: 'Description' },\n];\n\n/**\n * A button that opens a dialog to export the current catalog selection.\n *\n * @param settings - Optional export configuration settings including columns, custom exporters, and callbacks\n * @public\n */\nexport const CatalogExportButton = ({\n settings,\n}: {\n settings?: CatalogExportSettings;\n}) => {\n const { t } = useTranslationRef(catalogTranslationRef);\n const { exportStream, loading, error } = useStreamingExport();\n const [open, setOpen] = useState(false);\n const exporters = settings?.exporters;\n const disableBuiltinExporters = settings?.disableBuiltinExporters;\n const onSuccess = settings?.onSuccess;\n const onError = settings?.onError;\n const apis = useApiHolder();\n const toastApi = apis.get(toastApiRef)!;\n const [isExporting, setIsExporting] = useState(false);\n\n const effectiveColumns = settings?.columns ?? DEFAULT_EXPORT_COLUMNS;\n\n const allExportOptions = [\n ...(disableBuiltinExporters\n ? []\n : Object.values(CatalogExportType).map(format => ({\n value: format,\n label: format.toUpperCase(),\n }))),\n ...Object.entries(exporters ?? {}).map(([key, exporter]) => ({\n value: key,\n label: exporter.label ?? key.toUpperCase(),\n })),\n ];\n\n const [exportFormat, setExportFormat] = useState<string>(\n allExportOptions[0]?.value ?? '',\n );\n const [selectedColumnTitles, setSelectedColumnTitles] = useState<Set<string>>(\n () => new Set(effectiveColumns.map(c => getColumnTitle(c))),\n );\n const selectedColumns = effectiveColumns.filter(c =>\n selectedColumnTitles.has(getColumnTitle(c)),\n );\n\n const handleOpenDialog = () => {\n setSelectedColumnTitles(\n new Set(effectiveColumns.map(c => getColumnTitle(c))),\n );\n setOpen(true);\n };\n\n const toggleColumn = (title: string) => {\n setSelectedColumnTitles(prev => {\n const next = new Set(prev);\n if (next.has(title)) {\n next.delete(title);\n } else {\n next.add(title);\n }\n return next;\n });\n };\n\n useEffect(() => {\n if (isExporting && !loading) {\n if (error) {\n if (onError) {\n onError({ error });\n } else {\n toastApi.post({\n title: t('catalogExportButton.errorMessage', {\n errorMessage: error.message,\n }),\n status: 'danger',\n });\n }\n } else {\n if (onSuccess) {\n onSuccess();\n } else {\n toastApi.post({\n title: t('catalogExportButton.successMessage'),\n status: 'success',\n });\n }\n }\n setOpen(false);\n setIsExporting(false);\n }\n }, [isExporting, loading, error, toastApi, onSuccess, onError, t]);\n\n const handleExport = useCallback(async () => {\n setIsExporting(true);\n const exporterFn = exporters?.[exportFormat];\n await exportStream({\n exportFormat,\n filename: `catalog-export.${exportFormat}`,\n columns: selectedColumns,\n exporter: exporterFn?.exporter,\n });\n }, [exportFormat, exportStream, selectedColumns, exporters]);\n\n return (\n <>\n <Button\n variant=\"secondary\"\n iconStart={<RiDownloadLine />}\n onPress={handleOpenDialog}\n >\n {t('catalogExportButton.triggerButtonTitle')}\n </Button>\n\n <Dialog isOpen={open} onOpenChange={isOpen => !isOpen && setOpen(false)}>\n <DialogHeader>{t('catalogExportButton.dialogTitle')}</DialogHeader>\n <DialogBody>\n <Flex direction=\"column\" gap=\"4\">\n <Select\n data-testid=\"format-select\"\n label={t('catalogExportButton.formatLabel')}\n options={allExportOptions}\n value={exportFormat}\n onChange={key => setExportFormat(key as string)}\n />\n <Flex direction=\"column\" gap=\"1\">\n <Text variant=\"body-small\" weight=\"bold\">\n {t('catalogExportButton.columnsLabel')}\n </Text>\n {effectiveColumns.map(col => (\n <Checkbox\n key={col.entityFilterKey}\n isSelected={selectedColumnTitles.has(getColumnTitle(col))}\n onChange={() => toggleColumn(getColumnTitle(col))}\n >\n {getColumnTitle(col)}\n </Checkbox>\n ))}\n </Flex>\n </Flex>\n </DialogBody>\n <DialogFooter>\n <Button variant=\"secondary\" onPress={() => setOpen(false)}>\n {t('catalogExportButton.cancelButtonTitle')}\n </Button>\n <Button\n variant=\"primary\"\n onPress={() => handleExport()}\n isDisabled={\n !exportFormat || loading || selectedColumns.length === 0\n }\n loading={loading}\n >\n {loading\n ? t('catalogExportButton.exportingButtonTitle')\n : t('catalogExportButton.confirmButtonTitle')}\n </Button>\n </DialogFooter>\n </Dialog>\n </>\n );\n};\n"],"names":["CatalogExportType"],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ const streamDownload = async (stream, filename, contentType) => {
2
+ if ("showSaveFilePicker" in window) {
3
+ try {
4
+ const handle = await window.showSaveFilePicker({
5
+ suggestedName: filename,
6
+ types: [
7
+ {
8
+ description: "Export file",
9
+ accept: {
10
+ [contentType.split(";")[0]]: [`.${filename.split(".").pop()}`]
11
+ }
12
+ }
13
+ ]
14
+ });
15
+ const writable = await handle.createWritable();
16
+ await stream.pipeTo(writable);
17
+ return;
18
+ } catch (e) {
19
+ if (e.name === "AbortError") {
20
+ return;
21
+ }
22
+ }
23
+ }
24
+ const response = new Response(stream, {
25
+ headers: { "Content-Type": contentType }
26
+ });
27
+ const blob = await response.blob();
28
+ const blobUrl = URL.createObjectURL(blob);
29
+ const a = document.createElement("a");
30
+ a.href = blobUrl;
31
+ a.download = filename;
32
+ document.body.appendChild(a);
33
+ a.click();
34
+ a.remove();
35
+ URL.revokeObjectURL(blobUrl);
36
+ };
37
+ const createStreamFromAsyncGenerator = (generator) => {
38
+ const encoder = new TextEncoder();
39
+ return new ReadableStream({
40
+ async pull(controller) {
41
+ const result = await generator.next();
42
+ if (result.done) {
43
+ controller.close();
44
+ } else {
45
+ controller.enqueue(encoder.encode(result.value));
46
+ }
47
+ }
48
+ });
49
+ };
50
+
51
+ export { createStreamFromAsyncGenerator, streamDownload };
52
+ //# sourceMappingURL=downloadFile.esm.js.map