@backstage/plugin-app-visualizer 0.1.25-next.1 → 0.1.25-next.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @backstage/plugin-app-visualizer
2
2
 
3
+ ## 0.1.25-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 722e2df: Migrated to use `@backstage/ui`.
8
+ - Updated dependencies
9
+ - @backstage/ui@0.9.0-next.3
10
+ - @backstage/frontend-plugin-api@0.12.2-next.2
11
+ - @backstage/core-components@0.18.3-next.2
12
+
3
13
  ## 0.1.25-next.1
4
14
 
5
15
  ### Patch Changes
@@ -2,7 +2,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { Page, Header, Content, HeaderTabs } from '@backstage/core-components';
3
3
  import { useApi } from '@backstage/core-plugin-api';
4
4
  import { appTreeApiRef } from '@backstage/frontend-plugin-api';
5
- import Box from '@material-ui/core/Box';
5
+ import { Flex } from '@backstage/ui';
6
6
  import { useMemo, useCallback, useEffect } from 'react';
7
7
  import { DetailedVisualizer } from './DetailedVisualizer.esm.js';
8
8
  import { TextVisualizer } from './TextVisualizer.esm.js';
@@ -54,7 +54,7 @@ function AppVisualizerPage() {
54
54
  }, [element, navigate, tabs]);
55
55
  return /* @__PURE__ */ jsxs(Page, { themeId: "tool", children: [
56
56
  /* @__PURE__ */ jsx(Header, { title: "App Visualizer" }),
57
- /* @__PURE__ */ jsx(Content, { noPadding: true, stretch: true, children: /* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "column", height: "100%", children: [
57
+ /* @__PURE__ */ jsx(Content, { noPadding: true, stretch: true, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { height: "100%" }, children: [
58
58
  /* @__PURE__ */ jsx(
59
59
  HeaderTabs,
60
60
  {
@@ -1 +1 @@
1
- {"version":3,"file":"AppVisualizerPage.esm.js","sources":["../../../src/components/AppVisualizerPage/AppVisualizerPage.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 { Content, Header, HeaderTabs, Page } from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { appTreeApiRef } from '@backstage/frontend-plugin-api';\nimport Box from '@material-ui/core/Box';\nimport { useCallback, useEffect, useMemo } from 'react';\nimport { DetailedVisualizer } from './DetailedVisualizer';\nimport { TextVisualizer } from './TextVisualizer';\nimport { TreeVisualizer } from './TreeVisualizer';\nimport {\n matchRoutes,\n useLocation,\n useNavigate,\n useParams,\n useRoutes,\n} from 'react-router-dom';\n\nexport function AppVisualizerPage() {\n const appTreeApi = useApi(appTreeApiRef);\n const { tree } = appTreeApi.getTree();\n\n const tabs = useMemo(\n () => [\n {\n id: 'tree',\n path: 'tree',\n label: 'Tree',\n element: <TreeVisualizer tree={tree} />,\n },\n {\n id: 'detailed',\n path: 'detailed',\n label: 'Detailed',\n element: <DetailedVisualizer tree={tree} />,\n },\n {\n id: 'text',\n path: 'text',\n label: 'Text',\n element: <TextVisualizer tree={tree} />,\n },\n ],\n [tree],\n );\n\n const location = useLocation();\n const element = useRoutes(tabs, location);\n\n const currentPath = `/${useParams()['*']}`;\n const [matchedRoute] = matchRoutes(tabs, currentPath) ?? [];\n\n const currentTabIndex = matchedRoute\n ? tabs.findIndex(t => t.path === matchedRoute.route.path)\n : 0;\n\n const navigate = useNavigate();\n const handleTabChange = useCallback(\n (index: number) => {\n navigate(tabs[index].id);\n },\n [navigate, tabs],\n );\n\n useEffect(() => {\n if (!element) {\n navigate(tabs[0].path);\n }\n }, [element, navigate, tabs]);\n\n return (\n <Page themeId=\"tool\">\n <Header title=\"App Visualizer\" />\n <Content noPadding stretch>\n <Box display=\"flex\" flexDirection=\"column\" height=\"100%\">\n <HeaderTabs\n tabs={tabs}\n selectedIndex={currentTabIndex}\n onChange={handleTabChange}\n />\n {element}\n </Box>\n </Content>\n </Page>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AAgCO,SAAS,iBAAA,GAAoB;AAClC,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,UAAA,CAAW,OAAA,EAAQ;AAEpC,EAAA,MAAM,IAAA,GAAO,OAAA;AAAA,IACX,MAAM;AAAA,MACJ;AAAA,QACE,EAAA,EAAI,MAAA;AAAA,QACJ,IAAA,EAAM,MAAA;AAAA,QACN,KAAA,EAAO,MAAA;AAAA,QACP,OAAA,kBAAS,GAAA,CAAC,cAAA,EAAA,EAAe,IAAA,EAAY;AAAA,OACvC;AAAA,MACA;AAAA,QACE,EAAA,EAAI,UAAA;AAAA,QACJ,IAAA,EAAM,UAAA;AAAA,QACN,KAAA,EAAO,UAAA;AAAA,QACP,OAAA,kBAAS,GAAA,CAAC,kBAAA,EAAA,EAAmB,IAAA,EAAY;AAAA,OAC3C;AAAA,MACA;AAAA,QACE,EAAA,EAAI,MAAA;AAAA,QACJ,IAAA,EAAM,MAAA;AAAA,QACN,KAAA,EAAO,MAAA;AAAA,QACP,OAAA,kBAAS,GAAA,CAAC,cAAA,EAAA,EAAe,IAAA,EAAY;AAAA;AACvC,KACF;AAAA,IACA,CAAC,IAAI;AAAA,GACP;AAEA,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,IAAA,EAAM,QAAQ,CAAA;AAExC,EAAA,MAAM,WAAA,GAAc,CAAA,CAAA,EAAI,SAAA,EAAU,CAAE,GAAG,CAAC,CAAA,CAAA;AACxC,EAAA,MAAM,CAAC,YAAY,CAAA,GAAI,YAAY,IAAA,EAAM,WAAW,KAAK,EAAC;AAE1D,EAAA,MAAM,eAAA,GAAkB,YAAA,GACpB,IAAA,CAAK,SAAA,CAAU,CAAA,CAAA,KAAK,EAAE,IAAA,KAAS,YAAA,CAAa,KAAA,CAAM,IAAI,CAAA,GACtD,CAAA;AAEJ,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,MAAM,eAAA,GAAkB,WAAA;AAAA,IACtB,CAAC,KAAA,KAAkB;AACjB,MAAA,QAAA,CAAS,IAAA,CAAK,KAAK,CAAA,CAAE,EAAE,CAAA;AAAA,IACzB,CAAA;AAAA,IACA,CAAC,UAAU,IAAI;AAAA,GACjB;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,CAAE,IAAI,CAAA;AAAA,IACvB;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,QAAA,EAAU,IAAI,CAAC,CAAA;AAE5B,EAAA,uBACE,IAAA,CAAC,IAAA,EAAA,EAAK,OAAA,EAAQ,MAAA,EACZ,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,MAAA,EAAA,EAAO,OAAM,gBAAA,EAAiB,CAAA;AAAA,oBAC/B,GAAA,CAAC,OAAA,EAAA,EAAQ,SAAA,EAAS,IAAA,EAAC,OAAA,EAAO,IAAA,EACxB,QAAA,kBAAA,IAAA,CAAC,GAAA,EAAA,EAAI,OAAA,EAAQ,MAAA,EAAO,aAAA,EAAc,QAAA,EAAS,QAAO,MAAA,EAChD,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,IAAA;AAAA,UACA,aAAA,EAAe,eAAA;AAAA,UACf,QAAA,EAAU;AAAA;AAAA,OACZ;AAAA,MACC;AAAA,KAAA,EACH,CAAA,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"AppVisualizerPage.esm.js","sources":["../../../src/components/AppVisualizerPage/AppVisualizerPage.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 { Content, Header, HeaderTabs, Page } from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { appTreeApiRef } from '@backstage/frontend-plugin-api';\nimport { Flex } from '@backstage/ui';\nimport { useCallback, useEffect, useMemo } from 'react';\nimport { DetailedVisualizer } from './DetailedVisualizer';\nimport { TextVisualizer } from './TextVisualizer';\nimport { TreeVisualizer } from './TreeVisualizer';\nimport {\n matchRoutes,\n useLocation,\n useNavigate,\n useParams,\n useRoutes,\n} from 'react-router-dom';\n\nexport function AppVisualizerPage() {\n const appTreeApi = useApi(appTreeApiRef);\n const { tree } = appTreeApi.getTree();\n\n const tabs = useMemo(\n () => [\n {\n id: 'tree',\n path: 'tree',\n label: 'Tree',\n element: <TreeVisualizer tree={tree} />,\n },\n {\n id: 'detailed',\n path: 'detailed',\n label: 'Detailed',\n element: <DetailedVisualizer tree={tree} />,\n },\n {\n id: 'text',\n path: 'text',\n label: 'Text',\n element: <TextVisualizer tree={tree} />,\n },\n ],\n [tree],\n );\n\n const location = useLocation();\n const element = useRoutes(tabs, location);\n\n const currentPath = `/${useParams()['*']}`;\n const [matchedRoute] = matchRoutes(tabs, currentPath) ?? [];\n\n const currentTabIndex = matchedRoute\n ? tabs.findIndex(t => t.path === matchedRoute.route.path)\n : 0;\n\n const navigate = useNavigate();\n const handleTabChange = useCallback(\n (index: number) => {\n navigate(tabs[index].id);\n },\n [navigate, tabs],\n );\n\n useEffect(() => {\n if (!element) {\n navigate(tabs[0].path);\n }\n }, [element, navigate, tabs]);\n\n return (\n <Page themeId=\"tool\">\n <Header title=\"App Visualizer\" />\n <Content noPadding stretch>\n <Flex direction=\"column\" style={{ height: '100%' }}>\n <HeaderTabs\n tabs={tabs}\n selectedIndex={currentTabIndex}\n onChange={handleTabChange}\n />\n {element}\n </Flex>\n </Content>\n </Page>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AAgCO,SAAS,iBAAA,GAAoB;AAClC,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,UAAA,CAAW,OAAA,EAAQ;AAEpC,EAAA,MAAM,IAAA,GAAO,OAAA;AAAA,IACX,MAAM;AAAA,MACJ;AAAA,QACE,EAAA,EAAI,MAAA;AAAA,QACJ,IAAA,EAAM,MAAA;AAAA,QACN,KAAA,EAAO,MAAA;AAAA,QACP,OAAA,kBAAS,GAAA,CAAC,cAAA,EAAA,EAAe,IAAA,EAAY;AAAA,OACvC;AAAA,MACA;AAAA,QACE,EAAA,EAAI,UAAA;AAAA,QACJ,IAAA,EAAM,UAAA;AAAA,QACN,KAAA,EAAO,UAAA;AAAA,QACP,OAAA,kBAAS,GAAA,CAAC,kBAAA,EAAA,EAAmB,IAAA,EAAY;AAAA,OAC3C;AAAA,MACA;AAAA,QACE,EAAA,EAAI,MAAA;AAAA,QACJ,IAAA,EAAM,MAAA;AAAA,QACN,KAAA,EAAO,MAAA;AAAA,QACP,OAAA,kBAAS,GAAA,CAAC,cAAA,EAAA,EAAe,IAAA,EAAY;AAAA;AACvC,KACF;AAAA,IACA,CAAC,IAAI;AAAA,GACP;AAEA,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,IAAA,EAAM,QAAQ,CAAA;AAExC,EAAA,MAAM,WAAA,GAAc,CAAA,CAAA,EAAI,SAAA,EAAU,CAAE,GAAG,CAAC,CAAA,CAAA;AACxC,EAAA,MAAM,CAAC,YAAY,CAAA,GAAI,YAAY,IAAA,EAAM,WAAW,KAAK,EAAC;AAE1D,EAAA,MAAM,eAAA,GAAkB,YAAA,GACpB,IAAA,CAAK,SAAA,CAAU,CAAA,CAAA,KAAK,EAAE,IAAA,KAAS,YAAA,CAAa,KAAA,CAAM,IAAI,CAAA,GACtD,CAAA;AAEJ,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,MAAM,eAAA,GAAkB,WAAA;AAAA,IACtB,CAAC,KAAA,KAAkB;AACjB,MAAA,QAAA,CAAS,IAAA,CAAK,KAAK,CAAA,CAAE,EAAE,CAAA;AAAA,IACzB,CAAA;AAAA,IACA,CAAC,UAAU,IAAI;AAAA,GACjB;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,CAAE,IAAI,CAAA;AAAA,IACvB;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,QAAA,EAAU,IAAI,CAAC,CAAA;AAE5B,EAAA,uBACE,IAAA,CAAC,IAAA,EAAA,EAAK,OAAA,EAAQ,MAAA,EACZ,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,MAAA,EAAA,EAAO,OAAM,gBAAA,EAAiB,CAAA;AAAA,oBAC/B,GAAA,CAAC,OAAA,EAAA,EAAQ,SAAA,EAAS,IAAA,EAAC,SAAO,IAAA,EACxB,QAAA,kBAAA,IAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,QAAA,EAAS,KAAA,EAAO,EAAE,MAAA,EAAQ,QAAO,EAC/C,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,IAAA;AAAA,UACA,aAAA,EAAe,eAAA;AAAA,UACf,QAAA,EAAU;AAAA;AAAA,OACZ;AAAA,MACC;AAAA,KAAA,EACH,CAAA,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;;;;"}
@@ -1,126 +1,60 @@
1
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import { ThemeBlueprint, NavItemBlueprint, coreExtensionData, ApiBlueprint, useRouteRef } from '@backstage/frontend-plugin-api';
3
- import Box from '@material-ui/core/Box';
4
- import Paper from '@material-ui/core/Paper';
5
- import Tooltip from '@material-ui/core/Tooltip';
6
- import Typography from '@material-ui/core/Typography';
7
- import * as colors from '@material-ui/core/colors';
8
- import { makeStyles } from '@material-ui/core/styles';
9
- import InputIcon from '@material-ui/icons/InputSharp';
10
- import DisabledIcon from '@material-ui/icons/NotInterestedSharp';
11
- import { Link } from 'react-router-dom';
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { ThemeBlueprint, NavItemBlueprint, coreExtensionData, ApiBlueprint, useApi, routeResolutionApiRef } from '@backstage/frontend-plugin-api';
3
+ import { Flex, Box, TooltipTrigger, Text, Tooltip, Link } from '@backstage/ui';
4
+ import { RiCloseCircleLine, RiInputField } from '@remixicon/react';
5
+ import { Focusable } from 'react-aria-components';
12
6
 
7
+ function getContrastColor(bgColor) {
8
+ const hex = bgColor.replace("#", "");
9
+ const r = parseInt(hex.substr(0, 2), 16);
10
+ const g = parseInt(hex.substr(2, 2), 16);
11
+ const b = parseInt(hex.substr(4, 2), 16);
12
+ const brightness = (r * 299 + g * 587 + b * 114) / 1e3;
13
+ return brightness > 128 ? "#000000" : "#ffffff";
14
+ }
13
15
  function createOutputColorGenerator(colorMap, availableColors) {
14
16
  const map = /* @__PURE__ */ new Map();
15
17
  let i = 0;
16
18
  return function getOutputColor2(id) {
19
+ let backgroundColor;
17
20
  if (id in colorMap) {
18
- return colorMap[id];
19
- }
20
- let color = map.get(id);
21
- if (color) {
22
- return color;
21
+ backgroundColor = colorMap[id];
22
+ } else {
23
+ const cached = map.get(id);
24
+ if (cached) {
25
+ return cached;
26
+ }
27
+ backgroundColor = availableColors[i];
28
+ i += 1;
29
+ if (i >= availableColors.length) {
30
+ i = 0;
31
+ }
23
32
  }
24
- color = availableColors[i];
25
- i += 1;
26
- if (i >= availableColors.length) {
27
- i = 0;
28
- }
29
- map.set(id, color);
30
- return color;
33
+ const result = {
34
+ backgroundColor,
35
+ color: getContrastColor(backgroundColor)
36
+ };
37
+ map.set(id, result);
38
+ return result;
31
39
  };
32
40
  }
33
41
  const getOutputColor = createOutputColorGenerator(
34
42
  {
35
- [coreExtensionData.reactElement.id]: colors.green[500],
36
- [coreExtensionData.routePath.id]: colors.yellow[500],
37
- [coreExtensionData.routeRef.id]: colors.purple[500],
38
- [ApiBlueprint.dataRefs.factory.id]: colors.blue[500],
39
- [ThemeBlueprint.dataRefs.theme.id]: colors.lime[500],
40
- [NavItemBlueprint.dataRefs.target.id]: colors.orange[500]
43
+ [coreExtensionData.reactElement.id]: "#4caf50",
44
+ [coreExtensionData.routePath.id]: "#ffeb3b",
45
+ [coreExtensionData.routeRef.id]: "#9c27b0",
46
+ [ApiBlueprint.dataRefs.factory.id]: "#2196f3",
47
+ [ThemeBlueprint.dataRefs.theme.id]: "#cddc39",
48
+ [NavItemBlueprint.dataRefs.target.id]: "#ff9800"
41
49
  },
42
- [
43
- colors.blue[200],
44
- colors.orange[200],
45
- colors.green[200],
46
- colors.red[200],
47
- colors.yellow[200],
48
- colors.purple[200],
49
- colors.lime[200]
50
- ]
50
+ ["#90caf9", "#ffcc80", "#a5d6a7", "#ef9a9a", "#fff59d", "#ce93d8", "#e6ee9c"]
51
51
  );
52
- const config = {
53
- borderWidth: 0.75
54
- };
55
- const useStyles = makeStyles((theme) => ({
56
- extension: {
57
- borderLeftWidth: theme.spacing(config.borderWidth),
58
- borderLeftStyle: "solid",
59
- borderLeftColor: ({ depth }) => colors.grey[700 - depth % 6 * 100],
60
- cursor: "pointer",
61
- "&:hover $extensionHeader": {
62
- color: ({ enabled }) => enabled ? theme.palette.primary.main : theme.palette.text.secondary
63
- }
64
- },
65
- extensionHeader: {
66
- display: "flex",
67
- alignItems: "center",
68
- width: "fit-content",
69
- padding: theme.spacing(0.5, 1),
70
- color: ({ enabled }) => enabled ? theme.palette.text.primary : theme.palette.text.disabled,
71
- background: theme.palette.background.paper,
72
- borderTopRightRadius: theme.shape.borderRadius,
73
- borderBottomRightRadius: theme.shape.borderRadius
74
- },
75
- extensionHeaderId: {
76
- userSelect: "all"
77
- },
78
- extensionHeaderOutputs: {
79
- display: "flex",
80
- alignItems: "center",
81
- marginLeft: theme.spacing(1),
82
- gap: theme.spacing(1)
83
- },
84
- attachments: {
85
- gap: theme.spacing(2),
86
- display: "flex",
87
- flexDirection: "column"
88
- },
89
- attachmentsInput: {
90
- "&:first-child $attachmentsInputTitle": {
91
- borderTop: 0
92
- }
93
- },
94
- attachmentsInputTitle: {
95
- display: "flex",
96
- alignItems: "center",
97
- width: "fit-content",
98
- padding: theme.spacing(1),
99
- borderTopWidth: theme.spacing(config.borderWidth),
100
- borderTopStyle: "solid",
101
- borderTopColor: ({ depth }) => colors.grey[700 - depth % 6 * 100]
102
- },
103
- attachmentsInputName: {
104
- marginLeft: theme.spacing(1)
105
- },
106
- attachmentsInputChildren: {
107
- display: "flex",
108
- flexDirection: "column",
109
- alignItems: "flex-start",
110
- gap: theme.spacing(0.5),
111
- marginLeft: theme.spacing(1),
112
- marginBottom: theme.spacing(1)
113
- }
114
- }));
115
- const useOutputStyles = makeStyles((theme) => ({
116
- output: ({ color }) => ({
117
- padding: `0 10px`,
118
- height: 20,
119
- borderRadius: 10,
120
- color: theme.palette.getContrastText(color),
121
- backgroundColor: color
122
- })
123
- }));
52
+ function getBorderColor(depth) {
53
+ const greyLevels = [8, 7, 6, 5];
54
+ const index = depth % greyLevels.length;
55
+ const level = greyLevels[index];
56
+ return `var(--bui-gray-${level})`;
57
+ }
124
58
  function getFullPath(node) {
125
59
  if (!node) {
126
60
  return "";
@@ -132,46 +66,75 @@ function getFullPath(node) {
132
66
  }
133
67
  return getFullPath(parent) + part;
134
68
  }
135
- function OutputLink(props) {
136
- const routeRef = props.node?.instance?.getData(coreExtensionData.routeRef);
137
- try {
138
- const link = useRouteRef(routeRef);
139
- return /* @__PURE__ */ jsx(Tooltip, { title: /* @__PURE__ */ jsx(Typography, { children: props.dataRef.id }), children: /* @__PURE__ */ jsx(Box, { className: props.className, children: link ? /* @__PURE__ */ jsx(Link, { to: link(), children: "link" }) : null }) });
140
- } catch (ex) {
141
- console.warn(
142
- props.node?.spec.id ? `Unable to generate output link for ${props.node.spec.id}` : "Unable to generate output link",
143
- ex
144
- );
145
- return null;
146
- }
147
- }
148
69
  function Output(props) {
149
70
  const { dataRef, node } = props;
150
71
  const { id } = dataRef;
151
72
  const instance = node?.instance;
152
- const classes = useOutputStyles({ color: getOutputColor(id) });
153
- if (id === coreExtensionData.routePath.id) {
154
- return /* @__PURE__ */ jsx(Tooltip, { title: /* @__PURE__ */ jsx(Typography, { children: getFullPath(node) }), children: /* @__PURE__ */ jsx(Box, { className: classes.output, children: String(instance?.getData(dataRef) ?? "") }) });
73
+ const routeResolutionApi = useApi(routeResolutionApiRef);
74
+ const { backgroundColor, color } = getOutputColor(id);
75
+ const chipStyle = {
76
+ height: 20,
77
+ padding: "0 10px",
78
+ borderRadius: "10px",
79
+ color,
80
+ backgroundColor,
81
+ display: "flex",
82
+ alignItems: "center",
83
+ fontWeight: "var(--bui-font-weight-regular)"
84
+ };
85
+ if (id === coreExtensionData.routeRef.id && node) {
86
+ try {
87
+ const routeRef = props.node?.instance?.getData(
88
+ coreExtensionData.routeRef
89
+ );
90
+ const link = routeRef && routeResolutionApi.resolve(routeRef)?.();
91
+ if (link) {
92
+ return /* @__PURE__ */ jsxs(TooltipTrigger, { children: [
93
+ /* @__PURE__ */ jsx(Link, { href: link, style: chipStyle, children: "link" }),
94
+ /* @__PURE__ */ jsx(Tooltip, { children: id })
95
+ ] });
96
+ }
97
+ } catch {
98
+ }
155
99
  }
156
- if (id === coreExtensionData.routeRef.id) {
157
- return /* @__PURE__ */ jsx(OutputLink, { ...props, className: classes.output });
100
+ let tooltip = id;
101
+ let text = void 0;
102
+ if (id === coreExtensionData.routePath.id) {
103
+ text = String(instance?.getData(dataRef) ?? "");
104
+ tooltip = getFullPath(node);
158
105
  }
159
- return /* @__PURE__ */ jsx(Tooltip, { title: /* @__PURE__ */ jsx(Typography, { children: id }), children: /* @__PURE__ */ jsx(Box, { className: classes.output }) });
106
+ return /* @__PURE__ */ jsxs(TooltipTrigger, { children: [
107
+ /* @__PURE__ */ jsx(Focusable, { children: /* @__PURE__ */ jsx(Text, { style: { ...chipStyle, cursor: "help" }, children: text }) }),
108
+ /* @__PURE__ */ jsx(Tooltip, { style: { maxWidth: "unset" }, children: tooltip })
109
+ ] });
160
110
  }
161
111
  function Attachments(props) {
162
- const { node, enabled, depth } = props;
112
+ const { node, depth } = props;
163
113
  const { attachments } = node.edges;
164
- const classes = useStyles({ enabled, depth });
165
114
  if (attachments.size === 0) {
166
115
  return null;
167
116
  }
168
- return /* @__PURE__ */ jsx(Box, { className: classes.attachments, children: [...attachments.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, children]) => {
169
- return /* @__PURE__ */ jsxs(Box, { className: classes.attachmentsInput, children: [
170
- /* @__PURE__ */ jsxs(Box, { className: classes.attachmentsInputTitle, children: [
171
- /* @__PURE__ */ jsx(InputIcon, {}),
172
- /* @__PURE__ */ jsx(Typography, { className: classes.attachmentsInputName, children: key })
173
- ] }),
174
- /* @__PURE__ */ jsx(Box, { className: classes.attachmentsInputChildren, children: children.map((childNode) => /* @__PURE__ */ jsx(
117
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", gap: "4", children: [...attachments.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, children], idx) => {
118
+ return /* @__PURE__ */ jsxs(Box, { children: [
119
+ /* @__PURE__ */ jsxs(
120
+ Flex,
121
+ {
122
+ p: "2",
123
+ align: "center",
124
+ style: {
125
+ borderTopWidth: "var(--bui-space-1_5)",
126
+ borderTopStyle: "solid",
127
+ borderTopColor: getBorderColor(depth),
128
+ borderTop: idx === 0 ? "none" : void 0,
129
+ width: "fit-content"
130
+ },
131
+ children: [
132
+ /* @__PURE__ */ jsx(RiInputField, { size: 16 }),
133
+ /* @__PURE__ */ jsx("div", { style: { marginLeft: "var(--bui-space-2)" }, children: key })
134
+ ]
135
+ }
136
+ ),
137
+ /* @__PURE__ */ jsx(Flex, { ml: "2", mb: "2", direction: "column", align: "start", gap: "1", children: children.map((childNode) => /* @__PURE__ */ jsx(
175
138
  Extension,
176
139
  {
177
140
  node: childNode,
@@ -182,33 +145,59 @@ function Attachments(props) {
182
145
  ] }, key);
183
146
  }) });
184
147
  }
185
- function ExtensionTooltip(props) {
186
- const parts = [];
187
- let node = props.node;
188
- parts.push(node.spec.id);
189
- while (node.edges.attachedTo) {
190
- const input = node.edges.attachedTo.input;
191
- node = node.edges.attachedTo.node;
192
- parts.push(`${node.spec.id} [${input}]`);
193
- }
194
- parts.reverse();
195
- return /* @__PURE__ */ jsx(Fragment, { children: parts.map((part) => /* @__PURE__ */ jsx(Typography, { children: part }, part)) });
196
- }
197
148
  function Extension(props) {
198
149
  const { node, depth } = props;
199
150
  const enabled = Boolean(node.instance);
200
- const classes = useStyles({ enabled, depth });
201
151
  const dataRefs = node.instance && [...node.instance.getDataRefs()];
202
- return /* @__PURE__ */ jsxs(Box, { className: classes.extension, children: [
203
- /* @__PURE__ */ jsxs(Box, { className: classes.extensionHeader, children: [
204
- /* @__PURE__ */ jsx(Tooltip, { title: /* @__PURE__ */ jsx(ExtensionTooltip, { node }), children: /* @__PURE__ */ jsx(Typography, { className: classes.extensionHeaderId, children: node.spec.id }) }),
205
- /* @__PURE__ */ jsxs(Box, { className: classes.extensionHeaderOutputs, children: [
206
- dataRefs && dataRefs.length > 0 && dataRefs.sort((a, b) => a.id.localeCompare(b.id)).map((ref) => /* @__PURE__ */ jsx(Output, { dataRef: ref, node }, ref.id)),
207
- !enabled && /* @__PURE__ */ jsx(DisabledIcon, { fontSize: "small" })
208
- ] })
209
- ] }),
210
- /* @__PURE__ */ jsx(Attachments, { node, enabled, depth })
211
- ] }, node.spec.id);
152
+ const tooltipParts = [];
153
+ let currentNode = node;
154
+ tooltipParts.push(currentNode.spec.id);
155
+ while (currentNode.edges.attachedTo) {
156
+ const input = currentNode.edges.attachedTo.input;
157
+ currentNode = currentNode.edges.attachedTo.node;
158
+ tooltipParts.push(`${currentNode.spec.id} [${input}]`);
159
+ }
160
+ tooltipParts.reverse();
161
+ const tooltipText = tooltipParts.join("\n");
162
+ return /* @__PURE__ */ jsxs(
163
+ Box,
164
+ {
165
+ style: {
166
+ borderLeftWidth: "var(--bui-space-1_5)",
167
+ borderLeftStyle: "solid",
168
+ borderLeftColor: getBorderColor(depth)
169
+ },
170
+ children: [
171
+ /* @__PURE__ */ jsxs(
172
+ Flex,
173
+ {
174
+ py: "1",
175
+ px: "2",
176
+ align: "center",
177
+ style: {
178
+ width: "fit-content",
179
+ color: enabled ? "var(--bui-fg-primary)" : "var(--bui-fg-disabled)",
180
+ background: "var(--bui-bg-surface-1)",
181
+ borderTopRightRadius: "var(--bui-radius-2)",
182
+ borderBottomRightRadius: "var(--bui-radius-2)"
183
+ },
184
+ children: [
185
+ /* @__PURE__ */ jsxs(TooltipTrigger, { children: [
186
+ /* @__PURE__ */ jsx(Focusable, { children: /* @__PURE__ */ jsx(Text, { style: { userSelect: "all" }, children: node.spec.id }) }),
187
+ /* @__PURE__ */ jsx(Tooltip, { style: { maxWidth: "unset" }, children: /* @__PURE__ */ jsx(Text, { style: { whiteSpace: "pre-wrap" }, children: tooltipText }) })
188
+ ] }),
189
+ /* @__PURE__ */ jsxs(Flex, { ml: "2", align: "center", gap: "2", children: [
190
+ dataRefs && dataRefs.length > 0 && dataRefs.sort((a, b) => a.id.localeCompare(b.id)).map((ref) => /* @__PURE__ */ jsx(Output, { dataRef: ref, node }, ref.id)),
191
+ !enabled && /* @__PURE__ */ jsx(RiCloseCircleLine, { size: 16 })
192
+ ] })
193
+ ]
194
+ }
195
+ ),
196
+ /* @__PURE__ */ jsx(Attachments, { node, enabled, depth })
197
+ ]
198
+ },
199
+ node.spec.id
200
+ );
212
201
  }
213
202
  const legendMap = {
214
203
  "React Element": coreExtensionData.reactElement,
@@ -222,33 +211,36 @@ function Legend() {
222
211
  return /* @__PURE__ */ jsx(
223
212
  Box,
224
213
  {
225
- display: "grid",
226
- maxWidth: 600,
227
- p: 1,
214
+ p: "2",
228
215
  style: {
216
+ display: "grid",
217
+ maxWidth: 600,
229
218
  grid: "auto-flow / repeat(3, 1fr)",
230
- gap: 16
219
+ gap: "var(--bui-space-4)"
231
220
  },
232
- children: Object.entries(legendMap).map(([label, dataRef]) => /* @__PURE__ */ jsxs(
233
- Box,
234
- {
235
- display: "flex",
236
- style: { gap: 8 },
237
- alignItems: "center",
238
- children: [
239
- /* @__PURE__ */ jsx(Output, { dataRef }),
240
- /* @__PURE__ */ jsx(Typography, { children: label })
241
- ]
242
- },
243
- dataRef.id
244
- ))
221
+ children: Object.entries(legendMap).map(([label, dataRef]) => /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", children: [
222
+ /* @__PURE__ */ jsx(Output, { dataRef }),
223
+ /* @__PURE__ */ jsx("div", { children: label })
224
+ ] }, dataRef.id))
245
225
  }
246
226
  );
247
227
  }
248
228
  function DetailedVisualizer({ tree }) {
249
- return /* @__PURE__ */ jsxs(Box, { display: "flex", height: "100%", flex: "1 1 100%", flexDirection: "column", children: [
250
- /* @__PURE__ */ jsx(Box, { flex: "1 1 0", overflow: "auto", ml: 2, mt: 2, children: /* @__PURE__ */ jsx(Extension, { node: tree.root, depth: 0 }) }),
251
- /* @__PURE__ */ jsx(Box, { component: Paper, flex: "0 0 auto", m: 1, children: /* @__PURE__ */ jsx(Legend, {}) })
229
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { height: "100%", flex: "1 1 100%" }, children: [
230
+ /* @__PURE__ */ jsx(Box, { ml: "4", mt: "4", style: { flex: "1 1 0", overflow: "auto" }, children: /* @__PURE__ */ jsx(Extension, { node: tree.root, depth: 0 }) }),
231
+ /* @__PURE__ */ jsx(
232
+ Box,
233
+ {
234
+ m: "2",
235
+ style: {
236
+ flex: "0 0 auto",
237
+ background: "var(--bui-bg-surface-1)",
238
+ border: "1px solid var(--bui-border)",
239
+ borderRadius: "var(--bui-radius-2)"
240
+ },
241
+ children: /* @__PURE__ */ jsx(Legend, {})
242
+ }
243
+ )
252
244
  ] });
253
245
  }
254
246
 
@@ -1 +1 @@
1
- {"version":3,"file":"DetailedVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/DetailedVisualizer.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 {\n AppNode,\n AppTree,\n ExtensionDataRef,\n RouteRef,\n coreExtensionData,\n ApiBlueprint,\n NavItemBlueprint,\n ThemeBlueprint,\n useRouteRef,\n} from '@backstage/frontend-plugin-api';\nimport Box from '@material-ui/core/Box';\nimport Paper from '@material-ui/core/Paper';\nimport Tooltip from '@material-ui/core/Tooltip';\nimport Typography from '@material-ui/core/Typography';\nimport * as colors from '@material-ui/core/colors';\nimport { makeStyles } from '@material-ui/core/styles';\nimport InputIcon from '@material-ui/icons/InputSharp';\nimport DisabledIcon from '@material-ui/icons/NotInterestedSharp';\nimport { Link } from 'react-router-dom';\n\nfunction createOutputColorGenerator(\n colorMap: { [extDataId: string]: string },\n availableColors: string[],\n) {\n const map = new Map<string, string>();\n let i = 0;\n\n return function getOutputColor(id: string) {\n if (id in colorMap) {\n return colorMap[id];\n }\n let color = map.get(id);\n if (color) {\n return color;\n }\n color = availableColors[i];\n i += 1;\n if (i >= availableColors.length) {\n i = 0;\n }\n map.set(id, color);\n return color;\n };\n}\n\nconst getOutputColor = createOutputColorGenerator(\n {\n [coreExtensionData.reactElement.id]: colors.green[500],\n [coreExtensionData.routePath.id]: colors.yellow[500],\n [coreExtensionData.routeRef.id]: colors.purple[500],\n [ApiBlueprint.dataRefs.factory.id]: colors.blue[500],\n [ThemeBlueprint.dataRefs.theme.id]: colors.lime[500],\n [NavItemBlueprint.dataRefs.target.id]: colors.orange[500],\n },\n\n [\n colors.blue[200],\n colors.orange[200],\n colors.green[200],\n colors.red[200],\n colors.yellow[200],\n colors.purple[200],\n colors.lime[200],\n ],\n);\n\ninterface StyleProps {\n enabled: boolean;\n depth: number;\n}\n\nconst config = {\n borderWidth: 0.75,\n};\n\nconst useStyles = makeStyles(theme => ({\n extension: {\n borderLeftWidth: theme.spacing(config.borderWidth),\n borderLeftStyle: 'solid',\n borderLeftColor: ({ depth }: StyleProps) =>\n colors.grey[(700 - (depth % 6) * 100) as keyof typeof colors.grey],\n cursor: 'pointer',\n\n '&:hover $extensionHeader': {\n color: ({ enabled }: StyleProps) =>\n enabled ? theme.palette.primary.main : theme.palette.text.secondary,\n },\n },\n extensionHeader: {\n display: 'flex',\n alignItems: 'center',\n width: 'fit-content',\n\n padding: theme.spacing(0.5, 1),\n color: ({ enabled }: StyleProps) =>\n enabled ? theme.palette.text.primary : theme.palette.text.disabled,\n background: theme.palette.background.paper,\n\n borderTopRightRadius: theme.shape.borderRadius,\n borderBottomRightRadius: theme.shape.borderRadius,\n },\n extensionHeaderId: {\n userSelect: 'all',\n },\n extensionHeaderOutputs: {\n display: 'flex',\n alignItems: 'center',\n marginLeft: theme.spacing(1),\n gap: theme.spacing(1),\n },\n attachments: {\n gap: theme.spacing(2),\n display: 'flex',\n flexDirection: 'column',\n },\n attachmentsInput: {\n '&:first-child $attachmentsInputTitle': {\n borderTop: 0,\n },\n },\n attachmentsInputTitle: {\n display: 'flex',\n alignItems: 'center',\n width: 'fit-content',\n padding: theme.spacing(1),\n\n borderTopWidth: theme.spacing(config.borderWidth),\n borderTopStyle: 'solid',\n borderTopColor: ({ depth }: StyleProps) =>\n colors.grey[(700 - (depth % 6) * 100) as keyof typeof colors.grey],\n },\n attachmentsInputName: {\n marginLeft: theme.spacing(1),\n },\n attachmentsInputChildren: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'flex-start',\n gap: theme.spacing(0.5),\n marginLeft: theme.spacing(1),\n marginBottom: theme.spacing(1),\n },\n}));\n\nconst useOutputStyles = makeStyles(theme => ({\n output: ({ color }: { color: string }) => ({\n padding: `0 10px`,\n height: 20,\n borderRadius: 10,\n color: theme.palette.getContrastText(color),\n backgroundColor: color,\n }),\n}));\n\nfunction getFullPath(node?: AppNode): string {\n if (!node) {\n return '';\n }\n const parent = node.edges.attachedTo?.node;\n const part = node.instance?.getData(coreExtensionData.routePath);\n if (!part) {\n return getFullPath(parent);\n }\n return getFullPath(parent) + part;\n}\n\nfunction OutputLink(props: {\n dataRef: ExtensionDataRef<unknown>;\n node?: AppNode;\n className: string;\n}) {\n const routeRef = props.node?.instance?.getData(coreExtensionData.routeRef);\n\n try {\n const link = useRouteRef(routeRef as RouteRef<undefined>);\n\n return (\n <Tooltip title={<Typography>{props.dataRef.id}</Typography>}>\n <Box className={props.className}>\n {link ? <Link to={link()}>link</Link> : null}\n </Box>\n </Tooltip>\n );\n } catch (ex) {\n // eslint-disable-next-line no-console\n console.warn(\n props.node?.spec.id\n ? `Unable to generate output link for ${props.node.spec.id}`\n : 'Unable to generate output link',\n ex,\n );\n return null;\n }\n}\n\nfunction Output(props: { dataRef: ExtensionDataRef<unknown>; node?: AppNode }) {\n const { dataRef, node } = props;\n const { id } = dataRef;\n const instance = node?.instance;\n\n const classes = useOutputStyles({ color: getOutputColor(id) });\n\n if (id === coreExtensionData.routePath.id) {\n return (\n <Tooltip title={<Typography>{getFullPath(node)}</Typography>}>\n <Box className={classes.output}>\n {String(instance?.getData(dataRef) ?? '')}\n </Box>\n </Tooltip>\n );\n }\n\n if (id === coreExtensionData.routeRef.id) {\n return <OutputLink {...props} className={classes.output} />;\n }\n\n return (\n <Tooltip title={<Typography>{id}</Typography>}>\n <Box className={classes.output} />\n </Tooltip>\n );\n}\n\nfunction Attachments(props: {\n node: AppNode;\n enabled: boolean;\n depth: number;\n}) {\n const { node, enabled, depth } = props;\n const { attachments } = node.edges;\n\n const classes = useStyles({ enabled, depth });\n\n if (attachments.size === 0) {\n return null;\n }\n\n return (\n <Box className={classes.attachments}>\n {[...attachments.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, children]) => {\n return (\n <Box key={key} className={classes.attachmentsInput}>\n <Box className={classes.attachmentsInputTitle}>\n <InputIcon />\n <Typography className={classes.attachmentsInputName}>\n {key}\n </Typography>\n </Box>\n <Box className={classes.attachmentsInputChildren}>\n {children.map(childNode => (\n <Extension\n key={childNode.spec.id}\n node={childNode}\n depth={depth + 1}\n />\n ))}\n </Box>\n </Box>\n );\n })}\n </Box>\n );\n}\n\nfunction ExtensionTooltip(props: { node: AppNode }) {\n const parts = [];\n let node = props.node;\n parts.push(node.spec.id);\n while (node.edges.attachedTo) {\n const input = node.edges.attachedTo.input;\n node = node.edges.attachedTo.node;\n parts.push(`${node.spec.id} [${input}]`);\n }\n parts.reverse();\n\n return (\n <>\n {parts.map(part => (\n <Typography key={part}>{part}</Typography>\n ))}\n </>\n );\n}\n\nfunction Extension(props: { node: AppNode; depth: number }) {\n const { node, depth } = props;\n\n const enabled = Boolean(node.instance);\n const classes = useStyles({ enabled, depth });\n\n const dataRefs = node.instance && [...node.instance.getDataRefs()];\n\n return (\n <Box key={node.spec.id} className={classes.extension}>\n <Box className={classes.extensionHeader}>\n <Tooltip title={<ExtensionTooltip node={node} />}>\n <Typography className={classes.extensionHeaderId}>\n {node.spec.id}\n </Typography>\n </Tooltip>\n <Box className={classes.extensionHeaderOutputs}>\n {dataRefs &&\n dataRefs.length > 0 &&\n dataRefs\n .sort((a, b) => a.id.localeCompare(b.id))\n .map(ref => <Output key={ref.id} dataRef={ref} node={node} />)}\n {!enabled && <DisabledIcon fontSize=\"small\" />}\n </Box>\n </Box>\n <Attachments node={node} enabled={enabled} depth={depth} />\n </Box>\n );\n}\n\nconst legendMap = {\n 'React Element': coreExtensionData.reactElement,\n 'Utility API': ApiBlueprint.dataRefs.factory,\n 'Route Path': coreExtensionData.routePath,\n 'Route Ref': coreExtensionData.routeRef,\n 'Nav Target': NavItemBlueprint.dataRefs.target,\n Theme: ThemeBlueprint.dataRefs.theme,\n};\n\nfunction Legend() {\n return (\n <Box\n display=\"grid\"\n maxWidth={600}\n p={1}\n style={{\n grid: 'auto-flow / repeat(3, 1fr)',\n gap: 16,\n }}\n >\n {Object.entries(legendMap).map(([label, dataRef]) => (\n <Box\n key={dataRef.id}\n display=\"flex\"\n style={{ gap: 8 }}\n alignItems=\"center\"\n >\n <Output dataRef={dataRef} />\n <Typography>{label}</Typography>\n </Box>\n ))}\n </Box>\n );\n}\n\nexport function DetailedVisualizer({ tree }: { tree: AppTree }) {\n return (\n <Box display=\"flex\" height=\"100%\" flex=\"1 1 100%\" flexDirection=\"column\">\n <Box flex=\"1 1 0\" overflow=\"auto\" ml={2} mt={2}>\n <Extension node={tree.root} depth={0} />\n </Box>\n\n <Box component={Paper} flex=\"0 0 auto\" m={1}>\n <Legend />\n </Box>\n </Box>\n );\n}\n"],"names":["getOutputColor"],"mappings":"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"}
1
+ {"version":3,"file":"DetailedVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/DetailedVisualizer.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 {\n AppNode,\n AppTree,\n ExtensionDataRef,\n coreExtensionData,\n ApiBlueprint,\n NavItemBlueprint,\n ThemeBlueprint,\n useApi,\n routeResolutionApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { Box, Flex, Link, Text, Tooltip, TooltipTrigger } from '@backstage/ui';\nimport {\n RiInputField as InputIcon,\n RiCloseCircleLine as DisabledIcon,\n} from '@remixicon/react';\nimport { Focusable } from 'react-aria-components';\n\nfunction getContrastColor(bgColor: string): string {\n const hex = bgColor.replace('#', '');\n const r = parseInt(hex.substr(0, 2), 16);\n const g = parseInt(hex.substr(2, 2), 16);\n const b = parseInt(hex.substr(4, 2), 16);\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness > 128 ? '#000000' : '#ffffff';\n}\n\nfunction createOutputColorGenerator(\n colorMap: { [extDataId: string]: string },\n availableColors: string[],\n) {\n const map = new Map<string, { backgroundColor: string; color: string }>();\n let i = 0;\n\n return function getOutputColor(id: string) {\n let backgroundColor: string;\n if (id in colorMap) {\n backgroundColor = colorMap[id];\n } else {\n const cached = map.get(id);\n if (cached) {\n return cached;\n }\n backgroundColor = availableColors[i];\n i += 1;\n if (i >= availableColors.length) {\n i = 0;\n }\n }\n const result = {\n backgroundColor,\n color: getContrastColor(backgroundColor),\n };\n map.set(id, result);\n return result;\n };\n}\n\nconst getOutputColor = createOutputColorGenerator(\n {\n [coreExtensionData.reactElement.id]: '#4caf50',\n [coreExtensionData.routePath.id]: '#ffeb3b',\n [coreExtensionData.routeRef.id]: '#9c27b0',\n [ApiBlueprint.dataRefs.factory.id]: '#2196f3',\n [ThemeBlueprint.dataRefs.theme.id]: '#cddc39',\n [NavItemBlueprint.dataRefs.target.id]: '#ff9800',\n },\n\n ['#90caf9', '#ffcc80', '#a5d6a7', '#ef9a9a', '#fff59d', '#ce93d8', '#e6ee9c'],\n);\n\n// Helper function to get border color based on depth\nfunction getBorderColor(depth: number): string {\n const greyLevels = [8, 7, 6, 5]; // darker levels that contrast well with background\n const index = depth % greyLevels.length;\n const level = greyLevels[index];\n return `var(--bui-gray-${level})`;\n}\n\nfunction getFullPath(node?: AppNode): string {\n if (!node) {\n return '';\n }\n const parent = node.edges.attachedTo?.node;\n const part = node.instance?.getData(coreExtensionData.routePath);\n if (!part) {\n return getFullPath(parent);\n }\n return getFullPath(parent) + part;\n}\n\nfunction Output(props: { dataRef: ExtensionDataRef<unknown>; node?: AppNode }) {\n const { dataRef, node } = props;\n const { id } = dataRef;\n const instance = node?.instance;\n\n const routeResolutionApi = useApi(routeResolutionApiRef);\n\n const { backgroundColor, color } = getOutputColor(id);\n\n const chipStyle: React.CSSProperties = {\n height: 20,\n padding: '0 10px',\n borderRadius: '10px',\n color,\n backgroundColor,\n display: 'flex',\n alignItems: 'center',\n fontWeight:\n 'var(--bui-font-weight-regular)' as React.CSSProperties['fontWeight'],\n };\n\n if (id === coreExtensionData.routeRef.id && node) {\n try {\n const routeRef = props.node?.instance?.getData(\n coreExtensionData.routeRef,\n );\n const link = routeRef && routeResolutionApi.resolve(routeRef)?.();\n if (link) {\n return (\n <TooltipTrigger>\n <Link href={link} style={chipStyle}>\n link\n </Link>\n <Tooltip>{id}</Tooltip>\n </TooltipTrigger>\n );\n }\n } catch {\n /* ignore */\n }\n }\n\n let tooltip = id;\n let text: string | undefined = undefined;\n if (id === coreExtensionData.routePath.id) {\n text = String(instance?.getData(dataRef) ?? '');\n tooltip = getFullPath(node);\n }\n\n return (\n <TooltipTrigger>\n <Focusable>\n <Text style={{ ...chipStyle, cursor: 'help' }}>{text}</Text>\n </Focusable>\n <Tooltip style={{ maxWidth: 'unset' }}>{tooltip}</Tooltip>\n </TooltipTrigger>\n );\n}\n\nfunction Attachments(props: {\n node: AppNode;\n enabled: boolean;\n depth: number;\n}) {\n const { node, depth } = props;\n const { attachments } = node.edges;\n\n if (attachments.size === 0) {\n return null;\n }\n\n return (\n <Flex direction=\"column\" gap=\"4\">\n {[...attachments.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, children], idx) => {\n return (\n <Box key={key}>\n <Flex\n p=\"2\"\n align=\"center\"\n style={{\n borderTopWidth: 'var(--bui-space-1_5)',\n borderTopStyle: 'solid',\n borderTopColor: getBorderColor(depth),\n borderTop: idx === 0 ? 'none' : undefined,\n width: 'fit-content',\n }}\n >\n <InputIcon size={16} />\n <div style={{ marginLeft: 'var(--bui-space-2)' }}>{key}</div>\n </Flex>\n <Flex ml=\"2\" mb=\"2\" direction=\"column\" align=\"start\" gap=\"1\">\n {children.map(childNode => (\n <Extension\n key={childNode.spec.id}\n node={childNode}\n depth={depth + 1}\n />\n ))}\n </Flex>\n </Box>\n );\n })}\n </Flex>\n );\n}\n\nfunction Extension(props: { node: AppNode; depth: number }) {\n const { node, depth } = props;\n\n const enabled = Boolean(node.instance);\n const dataRefs = node.instance && [...node.instance.getDataRefs()];\n\n // Build tooltip text\n const tooltipParts = [];\n let currentNode = node;\n tooltipParts.push(currentNode.spec.id);\n while (currentNode.edges.attachedTo) {\n const input = currentNode.edges.attachedTo.input;\n currentNode = currentNode.edges.attachedTo.node;\n tooltipParts.push(`${currentNode.spec.id} [${input}]`);\n }\n tooltipParts.reverse();\n const tooltipText = tooltipParts.join('\\n');\n\n return (\n <Box\n key={node.spec.id}\n style={{\n borderLeftWidth: 'var(--bui-space-1_5)',\n borderLeftStyle: 'solid',\n borderLeftColor: getBorderColor(depth),\n }}\n >\n <Flex\n py=\"1\"\n px=\"2\"\n align=\"center\"\n style={{\n width: 'fit-content',\n color: enabled ? 'var(--bui-fg-primary)' : 'var(--bui-fg-disabled)',\n background: 'var(--bui-bg-surface-1)',\n borderTopRightRadius: 'var(--bui-radius-2)',\n borderBottomRightRadius: 'var(--bui-radius-2)',\n }}\n >\n <TooltipTrigger>\n <Focusable>\n <Text style={{ userSelect: 'all' }}>{node.spec.id}</Text>\n </Focusable>\n <Tooltip style={{ maxWidth: 'unset' }}>\n <Text style={{ whiteSpace: 'pre-wrap' }}>{tooltipText}</Text>\n </Tooltip>\n </TooltipTrigger>\n <Flex ml=\"2\" align=\"center\" gap=\"2\">\n {dataRefs &&\n dataRefs.length > 0 &&\n dataRefs\n .sort((a, b) => a.id.localeCompare(b.id))\n .map(ref => <Output key={ref.id} dataRef={ref} node={node} />)}\n {!enabled && <DisabledIcon size={16} />}\n </Flex>\n </Flex>\n <Attachments node={node} enabled={enabled} depth={depth} />\n </Box>\n );\n}\n\nconst legendMap = {\n 'React Element': coreExtensionData.reactElement,\n 'Utility API': ApiBlueprint.dataRefs.factory,\n 'Route Path': coreExtensionData.routePath,\n 'Route Ref': coreExtensionData.routeRef,\n 'Nav Target': NavItemBlueprint.dataRefs.target,\n Theme: ThemeBlueprint.dataRefs.theme,\n};\n\nfunction Legend() {\n return (\n <Box\n p=\"2\"\n style={{\n display: 'grid',\n maxWidth: 600,\n grid: 'auto-flow / repeat(3, 1fr)',\n gap: 'var(--bui-space-4)',\n }}\n >\n {Object.entries(legendMap).map(([label, dataRef]) => (\n <Flex key={dataRef.id} gap=\"2\" align=\"center\">\n <Output dataRef={dataRef} />\n <div>{label}</div>\n </Flex>\n ))}\n </Box>\n );\n}\n\nexport function DetailedVisualizer({ tree }: { tree: AppTree }) {\n return (\n <Flex direction=\"column\" style={{ height: '100%', flex: '1 1 100%' }}>\n <Box ml=\"4\" mt=\"4\" style={{ flex: '1 1 0', overflow: 'auto' }}>\n <Extension node={tree.root} depth={0} />\n </Box>\n\n <Box\n m=\"2\"\n style={{\n flex: '0 0 auto',\n background: 'var(--bui-bg-surface-1)',\n border: '1px solid var(--bui-border)',\n borderRadius: 'var(--bui-radius-2)',\n }}\n >\n <Legend />\n </Box>\n </Flex>\n );\n}\n"],"names":["getOutputColor","InputIcon","DisabledIcon"],"mappings":"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"}
@@ -1,8 +1,5 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
- import Box from '@material-ui/core/Box';
3
- import Checkbox from '@material-ui/core/Checkbox';
4
- import FormControlLabel from '@material-ui/core/FormControlLabel';
5
- import Paper from '@material-ui/core/Paper';
2
+ import { Box, Checkbox } from '@backstage/ui';
6
3
  import { useState } from 'react';
7
4
 
8
5
  function mkDiv(children, options) {
@@ -11,7 +8,7 @@ function mkDiv(children, options) {
11
8
  {
12
9
  style: {
13
10
  color: options?.color,
14
- marginLeft: options?.indent ? 16 : void 0
11
+ marginLeft: options?.indent ? "var(--bui-space-4)" : void 0
15
12
  },
16
13
  children
17
14
  },
@@ -50,35 +47,22 @@ function TextVisualizer({ tree }) {
50
47
  const [showOutputs, setShowOutputs] = useState(false);
51
48
  const [showDisabled, setShowDisabled] = useState(false);
52
49
  return /* @__PURE__ */ jsxs(Fragment, { children: [
53
- /* @__PURE__ */ jsx(Box, { style: { overflow: "auto", flex: "1 0 0" }, children: /* @__PURE__ */ jsx("div", { style: { margin: 16, width: "max-content" }, children: nodeToText(tree.root, { showOutputs, showDisabled }) }) }),
54
- /* @__PURE__ */ jsxs(Paper, { style: { padding: "8px 16px" }, children: [
55
- /* @__PURE__ */ jsx(
56
- FormControlLabel,
57
- {
58
- control: /* @__PURE__ */ jsx(
59
- Checkbox,
60
- {
61
- checked: showOutputs,
62
- onChange: (_, value) => setShowOutputs(value)
63
- }
64
- ),
65
- label: "Show Outputs"
66
- }
67
- ),
68
- /* @__PURE__ */ jsx(
69
- FormControlLabel,
70
- {
71
- control: /* @__PURE__ */ jsx(
72
- Checkbox,
73
- {
74
- checked: showDisabled,
75
- onChange: (_, value) => setShowDisabled(value)
76
- }
77
- ),
78
- label: "Show Disabled"
79
- }
80
- )
81
- ] })
50
+ /* @__PURE__ */ jsx(Box, { style: { overflow: "auto", flex: "1 0 0" }, children: /* @__PURE__ */ jsx(Box, { m: "4", style: { width: "max-content" }, children: nodeToText(tree.root, { showOutputs, showDisabled }) }) }),
51
+ /* @__PURE__ */ jsxs(
52
+ Box,
53
+ {
54
+ py: "2",
55
+ px: "4",
56
+ style: {
57
+ background: "var(--bui-bg-surface-1)",
58
+ borderTop: "1px solid var(--bui-border)"
59
+ },
60
+ children: [
61
+ /* @__PURE__ */ jsx(Checkbox, { isSelected: showOutputs, onChange: setShowOutputs, children: "Show Outputs" }),
62
+ /* @__PURE__ */ jsx(Checkbox, { isSelected: showDisabled, onChange: setShowDisabled, children: "Show Disabled" })
63
+ ]
64
+ }
65
+ )
82
66
  ] });
83
67
  }
84
68
 
@@ -1 +1 @@
1
- {"version":3,"file":"TextVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/TextVisualizer.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 { AppNode, AppTree } from '@backstage/frontend-plugin-api';\nimport Box from '@material-ui/core/Box';\nimport Checkbox from '@material-ui/core/Checkbox';\nimport FormControlLabel from '@material-ui/core/FormControlLabel';\nimport Paper from '@material-ui/core/Paper';\nimport { ReactNode, useState } from 'react';\n\nfunction mkDiv(\n children: ReactNode,\n options?: { indent?: boolean; key?: string | number; color?: string },\n) {\n return (\n <div\n key={options?.key}\n style={{\n color: options?.color,\n marginLeft: options?.indent ? 16 : undefined,\n }}\n >\n {children}\n </div>\n );\n}\n\nfunction nodeToText(\n node: AppNode,\n options?: { showOutputs?: boolean; showDisabled?: boolean },\n): ReactNode {\n const dataRefIds =\n node.instance && [...node.instance.getDataRefs()].map(r => r.id);\n const out =\n options?.showOutputs && dataRefIds && dataRefIds.length > 0\n ? ` out=\"${[...dataRefIds].sort().join(', ')}\"`\n : '';\n const color = node.instance ? undefined : 'gray';\n\n if (node.edges.attachments.size === 0) {\n return mkDiv(`<${node.spec.id}${out}/>`, { color });\n }\n\n return mkDiv([\n mkDiv(`<${node.spec.id}${out}>`, { key: 'start', color }),\n ...[...node.edges.attachments.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, v]) => {\n const children = v\n .filter(e => options?.showDisabled || e.instance)\n .sort((a, b) => a.spec.id.localeCompare(b.spec.id));\n if (children.length === 0) {\n return mkDiv(`${key} []`, { key, indent: true });\n }\n return mkDiv(\n [\n mkDiv(`${key} [`, { key: 'start' }),\n ...children.map(e =>\n mkDiv(nodeToText(e, options), { indent: true, key: e.spec.id }),\n ),\n mkDiv(']', { key: 'end' }),\n ],\n { key, indent: true },\n );\n }),\n mkDiv(`</${node.spec.id}>`, { key: 'end', color }),\n ]);\n}\n\nexport function TextVisualizer({ tree }: { tree: AppTree }) {\n const [showOutputs, setShowOutputs] = useState(false);\n const [showDisabled, setShowDisabled] = useState(false);\n\n return (\n <>\n <Box style={{ overflow: 'auto', flex: '1 0 0' }}>\n <div style={{ margin: 16, width: 'max-content' }}>\n {nodeToText(tree.root, { showOutputs, showDisabled })}\n </div>\n </Box>\n <Paper style={{ padding: '8px 16px' }}>\n <FormControlLabel\n control={\n <Checkbox\n checked={showOutputs}\n onChange={(_, value) => setShowOutputs(value)}\n />\n }\n label=\"Show Outputs\"\n />\n <FormControlLabel\n control={\n <Checkbox\n checked={showDisabled}\n onChange={(_, value) => setShowDisabled(value)}\n />\n }\n label=\"Show Disabled\"\n />\n </Paper>\n </>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TextVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/TextVisualizer.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 { AppNode, AppTree } from '@backstage/frontend-plugin-api';\nimport { Box, Checkbox } from '@backstage/ui';\nimport { ReactNode, useState } from 'react';\n\nfunction mkDiv(\n children: ReactNode,\n options?: { indent?: boolean; key?: string | number; color?: string },\n) {\n return (\n <div\n key={options?.key}\n style={{\n color: options?.color,\n marginLeft: options?.indent ? 'var(--bui-space-4)' : undefined,\n }}\n >\n {children}\n </div>\n );\n}\n\nfunction nodeToText(\n node: AppNode,\n options?: { showOutputs?: boolean; showDisabled?: boolean },\n): ReactNode {\n const dataRefIds =\n node.instance && [...node.instance.getDataRefs()].map(r => r.id);\n const out =\n options?.showOutputs && dataRefIds && dataRefIds.length > 0\n ? ` out=\"${[...dataRefIds].sort().join(', ')}\"`\n : '';\n const color = node.instance ? undefined : 'gray';\n\n if (node.edges.attachments.size === 0) {\n return mkDiv(`<${node.spec.id}${out}/>`, { color });\n }\n\n return mkDiv([\n mkDiv(`<${node.spec.id}${out}>`, { key: 'start', color }),\n ...[...node.edges.attachments.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, v]) => {\n const children = v\n .filter(e => options?.showDisabled || e.instance)\n .sort((a, b) => a.spec.id.localeCompare(b.spec.id));\n if (children.length === 0) {\n return mkDiv(`${key} []`, { key, indent: true });\n }\n return mkDiv(\n [\n mkDiv(`${key} [`, { key: 'start' }),\n ...children.map(e =>\n mkDiv(nodeToText(e, options), { indent: true, key: e.spec.id }),\n ),\n mkDiv(']', { key: 'end' }),\n ],\n { key, indent: true },\n );\n }),\n mkDiv(`</${node.spec.id}>`, { key: 'end', color }),\n ]);\n}\n\nexport function TextVisualizer({ tree }: { tree: AppTree }) {\n const [showOutputs, setShowOutputs] = useState(false);\n const [showDisabled, setShowDisabled] = useState(false);\n\n return (\n <>\n <Box style={{ overflow: 'auto', flex: '1 0 0' }}>\n <Box m=\"4\" style={{ width: 'max-content' }}>\n {nodeToText(tree.root, { showOutputs, showDisabled })}\n </Box>\n </Box>\n <Box\n py=\"2\"\n px=\"4\"\n style={{\n background: 'var(--bui-bg-surface-1)',\n borderTop: '1px solid var(--bui-border)',\n }}\n >\n <Checkbox isSelected={showOutputs} onChange={setShowOutputs}>\n Show Outputs\n </Checkbox>\n <Checkbox isSelected={showDisabled} onChange={setShowDisabled}>\n Show Disabled\n </Checkbox>\n </Box>\n </>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { DependencyGraph, DependencyGraphTypes } from '@backstage/core-components';
3
- import Box from '@material-ui/core/Box';
4
- import { makeStyles } from '@material-ui/core/styles';
3
+ import { Flex } from '@backstage/ui';
5
4
  import { useMemo, useState, useRef, useLayoutEffect } from 'react';
6
5
 
7
6
  function inputId({ node, input }) {
@@ -50,18 +49,8 @@ function resolveGraphData(tree) {
50
49
  ]
51
50
  };
52
51
  }
53
- const useStyles = makeStyles((theme) => ({
54
- node: {
55
- fill: (node) => node.type === "node" ? theme.palette.primary.light : theme.palette.grey[500],
56
- stroke: (node) => node.type === "node" ? theme.palette.primary.main : theme.palette.grey[600]
57
- },
58
- text: {
59
- fill: theme.palette.primary.contrastText
60
- }
61
- }));
62
52
  function Node(props) {
63
53
  const { node } = props;
64
- const classes = useStyles(node);
65
54
  const [width, setWidth] = useState(0);
66
55
  const [height, setHeight] = useState(0);
67
56
  const idRef = useRef(null);
@@ -79,11 +68,15 @@ function Node(props) {
79
68
  const padding = 10;
80
69
  const paddedWidth = width + padding * 2;
81
70
  const paddedHeight = height + padding * 2;
71
+ const nodeFill = node.type === "node" ? "#90caf9" : "#9e9e9e";
72
+ const nodeStroke = node.type === "node" ? "#2196f3" : "#757575";
73
+ const textFill = "#000000";
82
74
  return /* @__PURE__ */ jsxs("g", { children: [
83
75
  /* @__PURE__ */ jsx(
84
76
  "rect",
85
77
  {
86
- className: classes.node,
78
+ fill: nodeFill,
79
+ stroke: nodeStroke,
87
80
  width: paddedWidth,
88
81
  height: paddedHeight,
89
82
  rx: node.type === "node" ? 0 : 20
@@ -93,7 +86,7 @@ function Node(props) {
93
86
  "text",
94
87
  {
95
88
  ref: idRef,
96
- className: classes.text,
89
+ fill: textFill,
97
90
  y: paddedHeight / 2,
98
91
  x: paddedWidth / 2,
99
92
  textAnchor: "middle",
@@ -106,13 +99,14 @@ function Node(props) {
106
99
  function TreeVisualizer({ tree }) {
107
100
  const graphData = useMemo(() => resolveGraphData(tree), [tree]);
108
101
  return /* @__PURE__ */ jsx(
109
- Box,
102
+ Flex,
110
103
  {
111
- flex: "1 1 0",
112
- display: "flex",
113
- justifyContent: "stretch",
114
- alignItems: "stretch",
115
- overflow: "hidden",
104
+ style: {
105
+ flex: "1 1 0",
106
+ overflow: "hidden",
107
+ justifyContent: "stretch",
108
+ alignItems: "stretch"
109
+ },
116
110
  children: /* @__PURE__ */ jsx(
117
111
  DependencyGraph,
118
112
  {
@@ -1 +1 @@
1
- {"version":3,"file":"TreeVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/TreeVisualizer.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 {\n DependencyGraph,\n DependencyGraphTypes,\n} from '@backstage/core-components';\nimport { AppNode, AppTree } from '@backstage/frontend-plugin-api';\nimport Box from '@material-ui/core/Box';\nimport { makeStyles } from '@material-ui/core/styles';\nimport { useLayoutEffect, useMemo, useRef, useState } from 'react';\n\ntype NodeType =\n | ({ type: 'node'; id: string } & AppNode)\n | { type: 'input'; id: string; name: string };\n\nfunction inputId({ node, input }: { node: AppNode; input: string }) {\n return `${node.spec.id}$$${input}`;\n}\n\nfunction trimNodeId(id: string) {\n let newId = id;\n if (newId.startsWith('apis.')) {\n newId = newId.slice('apis.'.length);\n }\n if (newId.startsWith('plugin.')) {\n newId = newId.slice('plugin.'.length);\n }\n if (newId.startsWith('catalog.filter.entity.')) {\n newId = newId.slice('catalog.filter.entity.'.length);\n }\n if (newId.endsWith('.nav.index')) {\n newId = newId.slice(0, -'.nav.index'.length);\n }\n return newId;\n}\n\nfunction resolveGraphData(tree: AppTree): {\n nodes: NodeType[];\n edges: { from: string; to: string }[];\n} {\n const nodes = [...tree.nodes.values()]\n .filter(node => node.instance)\n .map(node => ({ ...node, id: node.spec.id, type: 'node' as const }));\n\n return {\n nodes: [\n ...nodes,\n ...nodes.flatMap(node =>\n [...node.edges.attachments.keys()].map(input => ({\n id: inputId({ node, input }),\n type: 'input' as const,\n name: input,\n })),\n ),\n ],\n edges: [\n ...nodes\n .filter(node => node.edges.attachedTo)\n .map(node => ({\n from: inputId(node.edges.attachedTo!),\n to: node.spec.id,\n })),\n ...nodes.flatMap(node =>\n [...node.edges.attachments.keys()].map(input => ({\n from: node.spec.id,\n to: inputId({ node, input }),\n })),\n ),\n ],\n };\n}\n\nconst useStyles = makeStyles(theme => ({\n node: {\n fill: (node: NodeType) =>\n node.type === 'node'\n ? theme.palette.primary.light\n : theme.palette.grey[500],\n stroke: (node: NodeType) =>\n node.type === 'node'\n ? theme.palette.primary.main\n : theme.palette.grey[600],\n },\n text: {\n fill: theme.palette.primary.contrastText,\n },\n}));\n\n/** @public */\nexport function Node(props: { node: NodeType }) {\n const { node } = props;\n const classes = useStyles(node);\n const [width, setWidth] = useState(0);\n const [height, setHeight] = useState(0);\n const idRef = useRef<SVGTextElement | null>(null);\n\n useLayoutEffect(() => {\n // set the width to the length of the ID\n if (idRef.current) {\n let { height: renderedHeight, width: renderedWidth } =\n idRef.current.getBBox();\n renderedHeight = Math.round(renderedHeight);\n renderedWidth = Math.round(renderedWidth);\n\n if (renderedHeight !== height || renderedWidth !== width) {\n setWidth(renderedWidth);\n setHeight(renderedHeight);\n }\n }\n }, [width, height]);\n\n const padding = 10;\n const paddedWidth = width + padding * 2;\n const paddedHeight = height + padding * 2;\n\n return (\n <g>\n <rect\n className={classes.node}\n width={paddedWidth}\n height={paddedHeight}\n rx={node.type === 'node' ? 0 : 20}\n />\n <text\n ref={idRef}\n className={classes.text}\n y={paddedHeight / 2}\n x={paddedWidth / 2}\n textAnchor=\"middle\"\n alignmentBaseline=\"middle\"\n >\n {node.type === 'node' ? trimNodeId(node.id) : node.name}\n </text>\n </g>\n );\n}\n\nexport function TreeVisualizer({ tree }: { tree: AppTree }) {\n const graphData = useMemo(() => resolveGraphData(tree), [tree]);\n\n return (\n <Box\n flex=\"1 1 0\"\n display=\"flex\"\n justifyContent=\"stretch\"\n alignItems=\"stretch\"\n overflow=\"hidden\"\n >\n <DependencyGraph\n fit=\"contain\"\n {...graphData}\n nodeMargin={10}\n rankMargin={50}\n paddingX={50}\n renderNode={Node}\n ranker={DependencyGraphTypes.Ranker.TIGHT_TREE}\n direction={DependencyGraphTypes.Direction.LEFT_RIGHT}\n />\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TreeVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/TreeVisualizer.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 {\n DependencyGraph,\n DependencyGraphTypes,\n} from '@backstage/core-components';\nimport { AppNode, AppTree } from '@backstage/frontend-plugin-api';\nimport { Flex } from '@backstage/ui';\nimport { useLayoutEffect, useMemo, useRef, useState } from 'react';\n\ntype NodeType =\n | ({ type: 'node'; id: string } & AppNode)\n | { type: 'input'; id: string; name: string };\n\nfunction inputId({ node, input }: { node: AppNode; input: string }) {\n return `${node.spec.id}$$${input}`;\n}\n\nfunction trimNodeId(id: string) {\n let newId = id;\n if (newId.startsWith('apis.')) {\n newId = newId.slice('apis.'.length);\n }\n if (newId.startsWith('plugin.')) {\n newId = newId.slice('plugin.'.length);\n }\n if (newId.startsWith('catalog.filter.entity.')) {\n newId = newId.slice('catalog.filter.entity.'.length);\n }\n if (newId.endsWith('.nav.index')) {\n newId = newId.slice(0, -'.nav.index'.length);\n }\n return newId;\n}\n\nfunction resolveGraphData(tree: AppTree): {\n nodes: NodeType[];\n edges: { from: string; to: string }[];\n} {\n const nodes = [...tree.nodes.values()]\n .filter(node => node.instance)\n .map(node => ({ ...node, id: node.spec.id, type: 'node' as const }));\n\n return {\n nodes: [\n ...nodes,\n ...nodes.flatMap(node =>\n [...node.edges.attachments.keys()].map(input => ({\n id: inputId({ node, input }),\n type: 'input' as const,\n name: input,\n })),\n ),\n ],\n edges: [\n ...nodes\n .filter(node => node.edges.attachedTo)\n .map(node => ({\n from: inputId(node.edges.attachedTo!),\n to: node.spec.id,\n })),\n ...nodes.flatMap(node =>\n [...node.edges.attachments.keys()].map(input => ({\n from: node.spec.id,\n to: inputId({ node, input }),\n })),\n ),\n ],\n };\n}\n\n/** @public */\nexport function Node(props: { node: NodeType }) {\n const { node } = props;\n const [width, setWidth] = useState(0);\n const [height, setHeight] = useState(0);\n const idRef = useRef<SVGTextElement | null>(null);\n\n useLayoutEffect(() => {\n // set the width to the length of the ID\n if (idRef.current) {\n let { height: renderedHeight, width: renderedWidth } =\n idRef.current.getBBox();\n renderedHeight = Math.round(renderedHeight);\n renderedWidth = Math.round(renderedWidth);\n\n if (renderedHeight !== height || renderedWidth !== width) {\n setWidth(renderedWidth);\n setHeight(renderedHeight);\n }\n }\n }, [width, height]);\n\n const padding = 10;\n const paddedWidth = width + padding * 2;\n const paddedHeight = height + padding * 2;\n\n // Simple inline styles for SVG elements\n const nodeFill = node.type === 'node' ? '#90caf9' : '#9e9e9e';\n const nodeStroke = node.type === 'node' ? '#2196f3' : '#757575';\n const textFill = '#000000';\n\n return (\n <g>\n <rect\n fill={nodeFill}\n stroke={nodeStroke}\n width={paddedWidth}\n height={paddedHeight}\n rx={node.type === 'node' ? 0 : 20}\n />\n <text\n ref={idRef}\n fill={textFill}\n y={paddedHeight / 2}\n x={paddedWidth / 2}\n textAnchor=\"middle\"\n alignmentBaseline=\"middle\"\n >\n {node.type === 'node' ? trimNodeId(node.id) : node.name}\n </text>\n </g>\n );\n}\n\nexport function TreeVisualizer({ tree }: { tree: AppTree }) {\n const graphData = useMemo(() => resolveGraphData(tree), [tree]);\n\n return (\n <Flex\n style={{\n flex: '1 1 0',\n overflow: 'hidden',\n justifyContent: 'stretch',\n alignItems: 'stretch',\n }}\n >\n <DependencyGraph\n fit=\"contain\"\n {...graphData}\n nodeMargin={10}\n rankMargin={50}\n paddingX={50}\n renderNode={Node}\n ranker={DependencyGraphTypes.Ranker.TIGHT_TREE}\n direction={DependencyGraphTypes.Direction.LEFT_RIGHT}\n />\n </Flex>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  var name = "@backstage/plugin-app-visualizer";
2
- var version = "0.1.25-next.1";
2
+ var version = "0.1.25-next.2";
3
3
  var description = "Visualizes the Backstage app structure";
4
4
  var backstage = {
5
5
  role: "frontend-plugin",
@@ -36,8 +36,9 @@ var dependencies = {
36
36
  "@backstage/core-components": "workspace:^",
37
37
  "@backstage/core-plugin-api": "workspace:^",
38
38
  "@backstage/frontend-plugin-api": "workspace:^",
39
- "@material-ui/core": "^4.12.2",
40
- "@material-ui/icons": "^4.9.1"
39
+ "@backstage/ui": "workspace:^",
40
+ "@remixicon/react": "^4.6.0",
41
+ "react-aria-components": "^1.13.0"
41
42
  };
42
43
  var devDependencies = {
43
44
  "@backstage/cli": "workspace:^",
@@ -1 +1 @@
1
- {"version":3,"file":"package.json.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"package.json.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,6 +1,6 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { createRouteRef, PageBlueprint, NavItemBlueprint, createFrontendPlugin } from '@backstage/frontend-plugin-api';
3
- import VisualizerIcon from '@material-ui/icons/Visibility';
3
+ import { RiEyeLine } from '@remixicon/react';
4
4
 
5
5
  const rootRouteRef = createRouteRef();
6
6
  const appVisualizerPage = PageBlueprint.make({
@@ -13,7 +13,7 @@ const appVisualizerPage = PageBlueprint.make({
13
13
  const appVisualizerNavItem = NavItemBlueprint.make({
14
14
  params: {
15
15
  title: "Visualizer",
16
- icon: VisualizerIcon,
16
+ icon: () => /* @__PURE__ */ jsx(RiEyeLine, {}),
17
17
  routeRef: rootRouteRef
18
18
  }
19
19
  });
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.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 {\n createFrontendPlugin,\n createRouteRef,\n NavItemBlueprint,\n PageBlueprint,\n} from '@backstage/frontend-plugin-api';\nimport VisualizerIcon from '@material-ui/icons/Visibility';\n\nconst rootRouteRef = createRouteRef();\n\nconst appVisualizerPage = PageBlueprint.make({\n params: {\n path: '/visualizer',\n routeRef: rootRouteRef,\n loader: () =>\n import('./components/AppVisualizerPage').then(m => (\n <m.AppVisualizerPage />\n )),\n },\n});\n\nexport const appVisualizerNavItem = NavItemBlueprint.make({\n params: {\n title: 'Visualizer',\n icon: VisualizerIcon,\n routeRef: rootRouteRef,\n },\n});\n\n/** @public */\nexport const visualizerPlugin = createFrontendPlugin({\n pluginId: 'app-visualizer',\n info: { packageJson: () => import('../package.json') },\n extensions: [appVisualizerPage, appVisualizerNavItem],\n});\n"],"names":[],"mappings":";;;;AAwBA,MAAM,eAAe,cAAA,EAAe;AAEpC,MAAM,iBAAA,GAAoB,cAAc,IAAA,CAAK;AAAA,EAC3C,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,aAAA;AAAA,IACN,QAAA,EAAU,YAAA;AAAA,IACV,MAAA,EAAQ,MACN,OAAO,6CAAgC,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,qBAC5C,GAAA,CAAC,CAAA,CAAE,iBAAA,EAAF,EAAoB,CACtB;AAAA;AAEP,CAAC,CAAA;AAEM,MAAM,oBAAA,GAAuB,iBAAiB,IAAA,CAAK;AAAA,EACxD,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,YAAA;AAAA,IACP,IAAA,EAAM,cAAA;AAAA,IACN,QAAA,EAAU;AAAA;AAEd,CAAC;AAGM,MAAM,mBAAmB,oBAAA,CAAqB;AAAA,EACnD,QAAA,EAAU,gBAAA;AAAA,EACV,MAAM,EAAE,WAAA,EAAa,MAAM,OAAO,uBAAiB,CAAA,EAAE;AAAA,EACrD,UAAA,EAAY,CAAC,iBAAA,EAAmB,oBAAoB;AACtD,CAAC;;;;"}
1
+ {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.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 {\n createFrontendPlugin,\n createRouteRef,\n NavItemBlueprint,\n PageBlueprint,\n} from '@backstage/frontend-plugin-api';\nimport { RiEyeLine as VisualizerIcon } from '@remixicon/react';\n\nconst rootRouteRef = createRouteRef();\n\nconst appVisualizerPage = PageBlueprint.make({\n params: {\n path: '/visualizer',\n routeRef: rootRouteRef,\n loader: () =>\n import('./components/AppVisualizerPage').then(m => (\n <m.AppVisualizerPage />\n )),\n },\n});\n\nexport const appVisualizerNavItem = NavItemBlueprint.make({\n params: {\n title: 'Visualizer',\n icon: () => <VisualizerIcon />,\n routeRef: rootRouteRef,\n },\n});\n\n/** @public */\nexport const visualizerPlugin = createFrontendPlugin({\n pluginId: 'app-visualizer',\n info: { packageJson: () => import('../package.json') },\n extensions: [appVisualizerPage, appVisualizerNavItem],\n});\n"],"names":["VisualizerIcon"],"mappings":";;;;AAwBA,MAAM,eAAe,cAAA,EAAe;AAEpC,MAAM,iBAAA,GAAoB,cAAc,IAAA,CAAK;AAAA,EAC3C,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,aAAA;AAAA,IACN,QAAA,EAAU,YAAA;AAAA,IACV,MAAA,EAAQ,MACN,OAAO,6CAAgC,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,qBAC5C,GAAA,CAAC,CAAA,CAAE,iBAAA,EAAF,EAAoB,CACtB;AAAA;AAEP,CAAC,CAAA;AAEM,MAAM,oBAAA,GAAuB,iBAAiB,IAAA,CAAK;AAAA,EACxD,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,YAAA;AAAA,IACP,IAAA,EAAM,sBAAM,GAAA,CAACA,SAAA,EAAA,EAAe,CAAA;AAAA,IAC5B,QAAA,EAAU;AAAA;AAEd,CAAC;AAGM,MAAM,mBAAmB,oBAAA,CAAqB;AAAA,EACnD,QAAA,EAAU,gBAAA;AAAA,EACV,MAAM,EAAE,WAAA,EAAa,MAAM,OAAO,uBAAiB,CAAA,EAAE;AAAA,EACrD,UAAA,EAAY,CAAC,iBAAA,EAAmB,oBAAoB;AACtD,CAAC;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-app-visualizer",
3
- "version": "0.1.25-next.1",
3
+ "version": "0.1.25-next.2",
4
4
  "description": "Visualizes the Backstage app structure",
5
5
  "backstage": {
6
6
  "role": "frontend-plugin",
@@ -37,11 +37,12 @@
37
37
  "test": "backstage-cli package test"
38
38
  },
39
39
  "dependencies": {
40
- "@backstage/core-components": "0.18.3-next.1",
40
+ "@backstage/core-components": "0.18.3-next.2",
41
41
  "@backstage/core-plugin-api": "1.11.2-next.1",
42
- "@backstage/frontend-plugin-api": "0.12.2-next.1",
43
- "@material-ui/core": "^4.12.2",
44
- "@material-ui/icons": "^4.9.1"
42
+ "@backstage/frontend-plugin-api": "0.12.2-next.2",
43
+ "@backstage/ui": "0.9.0-next.3",
44
+ "@remixicon/react": "^4.6.0",
45
+ "react-aria-components": "^1.13.0"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@backstage/cli": "0.34.5-next.1",