@backstage/core-components 0.18.11-next.0 → 0.18.11-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,19 @@
1
1
  # @backstage/core-components
2
2
 
3
+ ## 0.18.11-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - c161e1c: Lazy-load `react-syntax-highlighter` and `@dagrejs/dagre` so they are no longer pulled in eagerly through the barrel export. This reduces the upfront module cost of importing from `@backstage/core-components` by roughly 10 MB. The public API is unchanged.
8
+
9
+ ## 0.18.11-next.1
10
+
11
+ ### Patch Changes
12
+
13
+ - dbe93a7: Fix autologout not working correctly when closing all tabs
14
+ - Updated dependencies
15
+ - @backstage/core-plugin-api@1.12.7-next.0
16
+
3
17
  ## 0.18.11-next.0
4
18
 
5
19
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { useApi, identityApiRef, configApiRef } from '@backstage/core-plugin-api';
3
- import { useState, useEffect, useMemo } from 'react';
3
+ import { useState, useMemo, useEffect } from 'react';
4
4
  import { useIdleTimer, workerTimers } from 'react-idle-timer';
5
5
  import { LAST_SEEN_ONLINE_STORAGE_KEY, useLogoutDisconnectedUserEffect } from './disconnectedUsers.esm.js';
6
6
  import { StillTherePrompt } from './StillTherePrompt.esm.js';
@@ -28,6 +28,7 @@ const ConditionalAutoLogout = ({
28
28
  const onIdle = () => {
29
29
  setPromptOpen(false);
30
30
  setRemainingTimeCountdown(0);
31
+ lastSeenOnlineStore.delete();
31
32
  identityApi.signOut();
32
33
  };
33
34
  const onActive = () => {
@@ -94,17 +95,34 @@ const parseConfig = (configApi, props) => {
94
95
  const AutoLogout = (props) => {
95
96
  const identityApi = useApi(identityApiRef);
96
97
  const configApi = useApi(configApiRef);
97
- const [isLogged, setIsLogged] = useState(false);
98
+ const [isLogged, setIsLogged] = useState(null);
99
+ const lastSeenOnlineStore = useMemo(
100
+ () => new DefaultTimestampStore(LAST_SEEN_ONLINE_STORAGE_KEY),
101
+ []
102
+ );
98
103
  useEffect(() => {
99
- async function isLoggedIn(identity) {
100
- if ((await identity.getCredentials()).token) {
101
- setIsLogged(true);
102
- } else {
104
+ let cancelled = false;
105
+ async function checkLogin(identity) {
106
+ try {
107
+ const creds = await identity.getCredentials();
108
+ if (cancelled) return;
109
+ if (creds?.token) {
110
+ setIsLogged(true);
111
+ } else {
112
+ setIsLogged(false);
113
+ lastSeenOnlineStore.delete();
114
+ }
115
+ } catch (err) {
116
+ if (cancelled) return;
103
117
  setIsLogged(false);
118
+ lastSeenOnlineStore.delete();
104
119
  }
105
120
  }
106
- isLoggedIn(identityApi);
107
- }, [identityApi]);
121
+ checkLogin(identityApi);
122
+ return () => {
123
+ cancelled = true;
124
+ };
125
+ }, [lastSeenOnlineStore, identityApi]);
108
126
  const {
109
127
  enabled,
110
128
  idleTimeoutMinutes,
@@ -131,15 +149,12 @@ const AutoLogout = (props) => {
131
149
  );
132
150
  }
133
151
  }, [idleTimeoutMinutes, promptBeforeIdleSeconds]);
134
- const lastSeenOnlineStore = useMemo(
135
- () => new DefaultTimestampStore(LAST_SEEN_ONLINE_STORAGE_KEY),
136
- []
137
- );
138
152
  const [promptOpen, setPromptOpen] = useState(false);
139
153
  const [remainingTimeCountdown, setRemainingTimeCountdown] = useState(0);
140
154
  useLogoutDisconnectedUserEffect({
141
155
  enableEffect: logoutIfDisconnected,
142
- autologoutIsEnabled: enabled && isLogged,
156
+ autologoutIsEnabled: enabled,
157
+ isLoggedIn: isLogged,
143
158
  idleTimeoutSeconds: idleTimeoutMinutes * 60,
144
159
  lastSeenOnlineStore,
145
160
  identityApi
@@ -1 +1 @@
1
- {"version":3,"file":"AutoLogout.esm.js","sources":["../../../src/components/AutoLogout/AutoLogout.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 ConfigApi,\n configApiRef,\n IdentityApi,\n identityApiRef,\n useApi,\n} from '@backstage/core-plugin-api';\nimport { useEffect, useMemo, useState } from 'react';\nimport {\n EventsType,\n IIdleTimer,\n workerTimers,\n useIdleTimer,\n} from 'react-idle-timer';\n\nimport {\n LAST_SEEN_ONLINE_STORAGE_KEY,\n useLogoutDisconnectedUserEffect,\n} from './disconnectedUsers';\nimport { StillTherePrompt } from './StillTherePrompt';\nimport { DefaultTimestampStore, TimestampStore } from './timestampStore';\n\ntype AutoLogoutTrackableEvent = EventsType;\n\n/** @public */\nexport type AutoLogoutProps = {\n /**\n * Enable/disable the AutoLogoutMechanism.\n * defaults to true.\n */\n enabled?: boolean;\n /**\n * The amount of time (in minutes) of inactivity\n * after which the user is automatically logged out.\n * defaults to 60 minutes.\n */\n idleTimeoutMinutes?: number;\n /**\n * The number of seconds before the idleTimeout expires,\n * at which the user will be alerted by a Dialog that\n * they are about to be logged out.\n * defaults to 10 seconds\n */\n promptBeforeIdleSeconds?: number;\n /**\n * Enable/disable the usage of Node's worker thread timers instead of main thread timers.\n * This is helpful if you notice that the your browser is killing inactive tab's timers, like the one used by AutoLogout.\n * If you experience some browser incompatibility, you may try to set this to false.\n * defaults to true.\n */\n useWorkerTimers?: boolean;\n /**\n * Enable/disable the autologout for disconnected users.\n * disconnected users are the ones that are logged in but have no Backstage tab open in their browsers.\n * If enabled, disconnected users will be automatically logged out after `idleTimeoutMinutes`\n * defaults to true\n */\n logoutIfDisconnected?: boolean;\n};\n\ntype AutoLogoutInternalProps = Omit<Required<AutoLogoutProps>, 'enabled'> & {\n events: AutoLogoutTrackableEvent[];\n promptOpen: boolean;\n setPromptOpen: (value: boolean) => void;\n remainingTimeCountdown: number;\n setRemainingTimeCountdown: (amount: number) => void;\n identityApi: IdentityApi;\n lastSeenOnlineStore: TimestampStore;\n};\n\nconst ConditionalAutoLogout = ({\n idleTimeoutMinutes,\n events,\n useWorkerTimers,\n logoutIfDisconnected,\n promptBeforeIdleSeconds,\n promptOpen,\n setPromptOpen,\n remainingTimeCountdown,\n setRemainingTimeCountdown,\n identityApi,\n lastSeenOnlineStore,\n}: AutoLogoutInternalProps): JSX.Element => {\n const promptBeforeIdleMillis = promptBeforeIdleSeconds * 1000;\n const promptBeforeIdle = promptBeforeIdleMillis > 0 ? true : false;\n\n const onPrompt = async () => {\n // onPrompt will be called `promptBeforeIdle` milliseconds before `timeout`.\n // All events are disabled while the prompt is active.\n // If the user wishes to stay active, call the `activate()` method.\n // You can get the remaining prompt time with the `getRemainingTime()` method,\n setPromptOpen(true);\n setRemainingTimeCountdown(promptBeforeIdleMillis);\n };\n\n const onIdle = () => {\n // onIdle will be called after the timeout is reached.\n // Events will be rebound as long as `stopOnMount` is not set.\n setPromptOpen(false);\n setRemainingTimeCountdown(0);\n identityApi.signOut();\n };\n\n const onActive = () => {\n // onActive will only be called if `activate()` is called while `isPrompted()`\n // is true. Here you will also want to close your modal and perform\n // any active actions.\n setPromptOpen(false);\n setRemainingTimeCountdown(0);\n };\n\n const onAction = (\n _event?: Event | undefined,\n _idleTimer?: IIdleTimer | null,\n ) => {\n // onAction will be called if any user event is detected. The list of events that triggers a user event detection is the list of configured events\n // If any user event is detected we update the Last seen online in storage\n lastSeenOnlineStore.save(new Date());\n };\n\n const timer = useIdleTimer({\n timeout: idleTimeoutMinutes * 60 * 1000,\n events: events,\n crossTab: true,\n name: 'autologout-timer',\n timers: useWorkerTimers ? workerTimers : undefined,\n onIdle: onIdle,\n onActive: promptBeforeIdle ? onActive : undefined,\n onAction: logoutIfDisconnected ? onAction : undefined,\n onPrompt: promptBeforeIdle ? onPrompt : undefined,\n promptBeforeIdle: promptBeforeIdle ? promptBeforeIdleMillis : undefined,\n syncTimers: 1000,\n });\n\n return (\n <>\n {promptBeforeIdle && (\n <StillTherePrompt\n idleTimer={timer}\n open={promptOpen}\n setOpen={setPromptOpen}\n remainingTime={remainingTimeCountdown}\n setRemainingTime={setRemainingTimeCountdown}\n promptTimeoutMillis={promptBeforeIdleMillis}\n />\n )}\n </>\n );\n};\n\nconst defaultConfig: Required<AutoLogoutProps> = {\n enabled: true,\n idleTimeoutMinutes: 0.5,\n promptBeforeIdleSeconds: 10,\n useWorkerTimers: true,\n logoutIfDisconnected: true,\n};\n\n/**\n * A list of DOM events that the activity tracker will use to determine if the user is active or not.\n */\nconst defaultTrackedEvents: AutoLogoutTrackableEvent[] = [\n 'mousemove',\n 'keydown',\n 'wheel',\n 'DOMMouseScroll',\n 'mousewheel',\n 'mousedown',\n 'touchstart',\n 'touchmove',\n 'MSPointerDown',\n 'MSPointerMove',\n 'visibilitychange',\n];\n\n/**\n * Parses configuration for the AutoLogout. Properties configured in `app-config` take precedence over the props passed to the React component.\n * If neither props nor config properties are found, a default value will be set accordingly.\n */\nconst parseConfig = (\n configApi: ConfigApi,\n props: AutoLogoutProps,\n): Required<AutoLogoutProps> => {\n return {\n enabled:\n configApi.getOptionalBoolean('auth.autologout.enabled') ??\n props.enabled ??\n defaultConfig.enabled,\n idleTimeoutMinutes:\n configApi.getOptionalNumber('auth.autologout.idleTimeoutMinutes') ??\n props.idleTimeoutMinutes ??\n defaultConfig.idleTimeoutMinutes,\n promptBeforeIdleSeconds:\n configApi.getOptionalNumber('auth.autologout.promptBeforeIdleSeconds') ??\n props.promptBeforeIdleSeconds ??\n defaultConfig.promptBeforeIdleSeconds,\n useWorkerTimers:\n configApi.getOptionalBoolean('auth.autologout.useWorkerTimers') ??\n props.useWorkerTimers ??\n defaultConfig.useWorkerTimers,\n logoutIfDisconnected:\n configApi.getOptionalBoolean('auth.autologout.logoutIfDisconnected') ??\n props.logoutIfDisconnected ??\n defaultConfig.logoutIfDisconnected,\n };\n};\n\n/**\n * The Autologout feature enables platform engineers to add a mechanism to log out users after a configurable amount of time of inactivity.\n * When enabled, the mechanism will track user actions (mouse movement, mouse click, key pressing, taps, etc.) in order to determine if they are active or not.\n * After a certain amount of inactivity/idle time, the user session is invalidated and they are required to sign in again.\n *\n * @public\n */\nexport const AutoLogout = (props: AutoLogoutProps): JSX.Element | null => {\n const identityApi = useApi(identityApiRef);\n const configApi = useApi(configApiRef);\n const [isLogged, setIsLogged] = useState(false);\n useEffect(() => {\n // if the user is not logged in, the autologout feature won't affect the app even if enabled\n async function isLoggedIn(identity: IdentityApi) {\n if ((await identity.getCredentials()).token) {\n setIsLogged(true);\n } else {\n setIsLogged(false);\n }\n }\n isLoggedIn(identityApi);\n }, [identityApi]);\n\n const {\n enabled,\n idleTimeoutMinutes,\n promptBeforeIdleSeconds,\n logoutIfDisconnected,\n useWorkerTimers,\n }: AutoLogoutProps = useMemo(() => {\n return parseConfig(configApi, props);\n }, [configApi, props]);\n\n useEffect(() => {\n if (idleTimeoutMinutes < 0.5) {\n throw new Error(\n '❌ idleTimeoutMinutes property should be >= 0.5 minutes (30 seconds).',\n );\n }\n\n if (promptBeforeIdleSeconds < 0) {\n throw new Error(\n '❌ promptBeforeIdleSeconds property should be >= 0 seconds. Set to 0 to disable the prompt.',\n );\n }\n\n if (idleTimeoutMinutes * 60 <= promptBeforeIdleSeconds) {\n throw new Error(\n `❌ promptBeforeIdleSeconds should be smaller than idleTimeoutMinutes`,\n );\n }\n }, [idleTimeoutMinutes, promptBeforeIdleSeconds]);\n\n const lastSeenOnlineStore: TimestampStore = useMemo(\n () => new DefaultTimestampStore(LAST_SEEN_ONLINE_STORAGE_KEY),\n [],\n );\n const [promptOpen, setPromptOpen] = useState<boolean>(false);\n\n const [remainingTimeCountdown, setRemainingTimeCountdown] =\n useState<number>(0);\n\n useLogoutDisconnectedUserEffect({\n enableEffect: logoutIfDisconnected,\n autologoutIsEnabled: enabled && isLogged,\n idleTimeoutSeconds: idleTimeoutMinutes * 60,\n lastSeenOnlineStore,\n identityApi,\n });\n\n if (!enabled || !isLogged) {\n return null;\n }\n\n return (\n <ConditionalAutoLogout\n idleTimeoutMinutes={idleTimeoutMinutes}\n promptBeforeIdleSeconds={promptBeforeIdleSeconds}\n useWorkerTimers={useWorkerTimers}\n events={defaultTrackedEvents}\n logoutIfDisconnected={logoutIfDisconnected}\n promptOpen={promptOpen}\n setPromptOpen={setPromptOpen}\n remainingTimeCountdown={remainingTimeCountdown}\n setRemainingTimeCountdown={setRemainingTimeCountdown}\n identityApi={identityApi}\n lastSeenOnlineStore={lastSeenOnlineStore}\n />\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AutoLogout.esm.js","sources":["../../../src/components/AutoLogout/AutoLogout.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 ConfigApi,\n configApiRef,\n IdentityApi,\n identityApiRef,\n useApi,\n} from '@backstage/core-plugin-api';\nimport { useEffect, useMemo, useState } from 'react';\nimport {\n EventsType,\n IIdleTimer,\n workerTimers,\n useIdleTimer,\n} from 'react-idle-timer';\n\nimport {\n LAST_SEEN_ONLINE_STORAGE_KEY,\n useLogoutDisconnectedUserEffect,\n} from './disconnectedUsers';\nimport { StillTherePrompt } from './StillTherePrompt';\nimport { DefaultTimestampStore, TimestampStore } from './timestampStore';\n\ntype AutoLogoutTrackableEvent = EventsType;\n\n/** @public */\nexport type AutoLogoutProps = {\n /**\n * Enable/disable the AutoLogoutMechanism.\n * defaults to true.\n */\n enabled?: boolean;\n /**\n * The amount of time (in minutes) of inactivity\n * after which the user is automatically logged out.\n * defaults to 60 minutes.\n */\n idleTimeoutMinutes?: number;\n /**\n * The number of seconds before the idleTimeout expires,\n * at which the user will be alerted by a Dialog that\n * they are about to be logged out.\n * defaults to 10 seconds\n */\n promptBeforeIdleSeconds?: number;\n /**\n * Enable/disable the usage of Node's worker thread timers instead of main thread timers.\n * This is helpful if you notice that the your browser is killing inactive tab's timers, like the one used by AutoLogout.\n * If you experience some browser incompatibility, you may try to set this to false.\n * defaults to true.\n */\n useWorkerTimers?: boolean;\n /**\n * Enable/disable the autologout for disconnected users.\n * disconnected users are the ones that are logged in but have no Backstage tab open in their browsers.\n * If enabled, disconnected users will be automatically logged out after `idleTimeoutMinutes`\n * defaults to true\n */\n logoutIfDisconnected?: boolean;\n};\n\ntype AutoLogoutInternalProps = Omit<Required<AutoLogoutProps>, 'enabled'> & {\n events: AutoLogoutTrackableEvent[];\n promptOpen: boolean;\n setPromptOpen: (value: boolean) => void;\n remainingTimeCountdown: number;\n setRemainingTimeCountdown: (amount: number) => void;\n identityApi: IdentityApi;\n lastSeenOnlineStore: TimestampStore;\n};\n\nconst ConditionalAutoLogout = ({\n idleTimeoutMinutes,\n events,\n useWorkerTimers,\n logoutIfDisconnected,\n promptBeforeIdleSeconds,\n promptOpen,\n setPromptOpen,\n remainingTimeCountdown,\n setRemainingTimeCountdown,\n identityApi,\n lastSeenOnlineStore,\n}: AutoLogoutInternalProps): JSX.Element => {\n const promptBeforeIdleMillis = promptBeforeIdleSeconds * 1000;\n const promptBeforeIdle = promptBeforeIdleMillis > 0 ? true : false;\n\n const onPrompt = async () => {\n // onPrompt will be called `promptBeforeIdle` milliseconds before `timeout`.\n // All events are disabled while the prompt is active.\n // If the user wishes to stay active, call the `activate()` method.\n // You can get the remaining prompt time with the `getRemainingTime()` method,\n setPromptOpen(true);\n setRemainingTimeCountdown(promptBeforeIdleMillis);\n };\n\n const onIdle = () => {\n // onIdle will be called after the timeout is reached.\n // Events will be rebound as long as `stopOnMount` is not set.\n setPromptOpen(false);\n setRemainingTimeCountdown(0);\n lastSeenOnlineStore.delete();\n identityApi.signOut();\n };\n\n const onActive = () => {\n // onActive will only be called if `activate()` is called while `isPrompted()`\n // is true. Here you will also want to close your modal and perform\n // any active actions.\n setPromptOpen(false);\n setRemainingTimeCountdown(0);\n };\n\n const onAction = (\n _event?: Event | undefined,\n _idleTimer?: IIdleTimer | null,\n ) => {\n // onAction will be called if any user event is detected. The list of events that triggers a user event detection is the list of configured events\n // If any user event is detected we update the Last seen online in storage\n lastSeenOnlineStore.save(new Date());\n };\n\n const timer = useIdleTimer({\n timeout: idleTimeoutMinutes * 60 * 1000,\n events: events,\n crossTab: true,\n name: 'autologout-timer',\n timers: useWorkerTimers ? workerTimers : undefined,\n onIdle: onIdle,\n onActive: promptBeforeIdle ? onActive : undefined,\n onAction: logoutIfDisconnected ? onAction : undefined,\n onPrompt: promptBeforeIdle ? onPrompt : undefined,\n promptBeforeIdle: promptBeforeIdle ? promptBeforeIdleMillis : undefined,\n syncTimers: 1000,\n });\n\n return (\n <>\n {promptBeforeIdle && (\n <StillTherePrompt\n idleTimer={timer}\n open={promptOpen}\n setOpen={setPromptOpen}\n remainingTime={remainingTimeCountdown}\n setRemainingTime={setRemainingTimeCountdown}\n promptTimeoutMillis={promptBeforeIdleMillis}\n />\n )}\n </>\n );\n};\n\nconst defaultConfig: Required<AutoLogoutProps> = {\n enabled: true,\n idleTimeoutMinutes: 0.5,\n promptBeforeIdleSeconds: 10,\n useWorkerTimers: true,\n logoutIfDisconnected: true,\n};\n\n/**\n * A list of DOM events that the activity tracker will use to determine if the user is active or not.\n */\nconst defaultTrackedEvents: AutoLogoutTrackableEvent[] = [\n 'mousemove',\n 'keydown',\n 'wheel',\n 'DOMMouseScroll',\n 'mousewheel',\n 'mousedown',\n 'touchstart',\n 'touchmove',\n 'MSPointerDown',\n 'MSPointerMove',\n 'visibilitychange',\n];\n\n/**\n * Parses configuration for the AutoLogout. Properties configured in `app-config` take precedence over the props passed to the React component.\n * If neither props nor config properties are found, a default value will be set accordingly.\n */\nconst parseConfig = (\n configApi: ConfigApi,\n props: AutoLogoutProps,\n): Required<AutoLogoutProps> => {\n return {\n enabled:\n configApi.getOptionalBoolean('auth.autologout.enabled') ??\n props.enabled ??\n defaultConfig.enabled,\n idleTimeoutMinutes:\n configApi.getOptionalNumber('auth.autologout.idleTimeoutMinutes') ??\n props.idleTimeoutMinutes ??\n defaultConfig.idleTimeoutMinutes,\n promptBeforeIdleSeconds:\n configApi.getOptionalNumber('auth.autologout.promptBeforeIdleSeconds') ??\n props.promptBeforeIdleSeconds ??\n defaultConfig.promptBeforeIdleSeconds,\n useWorkerTimers:\n configApi.getOptionalBoolean('auth.autologout.useWorkerTimers') ??\n props.useWorkerTimers ??\n defaultConfig.useWorkerTimers,\n logoutIfDisconnected:\n configApi.getOptionalBoolean('auth.autologout.logoutIfDisconnected') ??\n props.logoutIfDisconnected ??\n defaultConfig.logoutIfDisconnected,\n };\n};\n\n/**\n * The Autologout feature enables platform engineers to add a mechanism to log out users after a configurable amount of time of inactivity.\n * When enabled, the mechanism will track user actions (mouse movement, mouse click, key pressing, taps, etc.) in order to determine if they are active or not.\n * After a certain amount of inactivity/idle time, the user session is invalidated and they are required to sign in again.\n *\n * @public\n */\nexport const AutoLogout = (props: AutoLogoutProps): JSX.Element | null => {\n const identityApi = useApi(identityApiRef);\n const configApi = useApi(configApiRef);\n const [isLogged, setIsLogged] = useState<boolean | null>(null);\n const lastSeenOnlineStore: TimestampStore = useMemo(\n () => new DefaultTimestampStore(LAST_SEEN_ONLINE_STORAGE_KEY),\n [],\n );\n\n useEffect(() => {\n let cancelled = false;\n\n async function checkLogin(identity: IdentityApi) {\n try {\n const creds = await identity.getCredentials();\n if (cancelled) return;\n if (creds?.token) {\n setIsLogged(true);\n } else {\n setIsLogged(false);\n lastSeenOnlineStore.delete();\n }\n } catch (err) {\n if (cancelled) return;\n setIsLogged(false);\n lastSeenOnlineStore.delete();\n }\n }\n checkLogin(identityApi);\n\n return () => {\n cancelled = true;\n };\n }, [lastSeenOnlineStore, identityApi]);\n\n const {\n enabled,\n idleTimeoutMinutes,\n promptBeforeIdleSeconds,\n logoutIfDisconnected,\n useWorkerTimers,\n }: AutoLogoutProps = useMemo(() => {\n return parseConfig(configApi, props);\n }, [configApi, props]);\n\n useEffect(() => {\n if (idleTimeoutMinutes < 0.5) {\n throw new Error(\n '❌ idleTimeoutMinutes property should be >= 0.5 minutes (30 seconds).',\n );\n }\n\n if (promptBeforeIdleSeconds < 0) {\n throw new Error(\n '❌ promptBeforeIdleSeconds property should be >= 0 seconds. Set to 0 to disable the prompt.',\n );\n }\n\n if (idleTimeoutMinutes * 60 <= promptBeforeIdleSeconds) {\n throw new Error(\n `❌ promptBeforeIdleSeconds should be smaller than idleTimeoutMinutes`,\n );\n }\n }, [idleTimeoutMinutes, promptBeforeIdleSeconds]);\n\n const [promptOpen, setPromptOpen] = useState<boolean>(false);\n\n const [remainingTimeCountdown, setRemainingTimeCountdown] =\n useState<number>(0);\n\n useLogoutDisconnectedUserEffect({\n enableEffect: logoutIfDisconnected,\n autologoutIsEnabled: enabled,\n isLoggedIn: isLogged,\n idleTimeoutSeconds: idleTimeoutMinutes * 60,\n lastSeenOnlineStore,\n identityApi,\n });\n\n if (!enabled || !isLogged) {\n return null;\n }\n\n return (\n <ConditionalAutoLogout\n idleTimeoutMinutes={idleTimeoutMinutes}\n promptBeforeIdleSeconds={promptBeforeIdleSeconds}\n useWorkerTimers={useWorkerTimers}\n events={defaultTrackedEvents}\n logoutIfDisconnected={logoutIfDisconnected}\n promptOpen={promptOpen}\n setPromptOpen={setPromptOpen}\n remainingTimeCountdown={remainingTimeCountdown}\n setRemainingTimeCountdown={setRemainingTimeCountdown}\n identityApi={identityApi}\n lastSeenOnlineStore={lastSeenOnlineStore}\n />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -4,30 +4,36 @@ const LAST_SEEN_ONLINE_STORAGE_KEY = "@backstage/autologout:lastSeenOnline";
4
4
  const useLogoutDisconnectedUserEffect = ({
5
5
  enableEffect,
6
6
  autologoutIsEnabled,
7
+ isLoggedIn,
7
8
  idleTimeoutSeconds,
8
9
  lastSeenOnlineStore,
9
10
  identityApi
10
11
  }) => {
11
12
  useEffect(() => {
12
- if (autologoutIsEnabled && enableEffect) {
13
- const lastSeenOnline = lastSeenOnlineStore.get();
14
- if (lastSeenOnline) {
15
- const now = /* @__PURE__ */ new Date();
16
- const nowSeconds = Math.ceil(now.getTime() / 1e3);
17
- const lastSeenOnlineSeconds = Math.ceil(
18
- lastSeenOnline.getTime() / 1e3
19
- );
20
- if (nowSeconds - lastSeenOnlineSeconds > idleTimeoutSeconds) {
21
- identityApi.signOut();
22
- }
23
- }
24
- lastSeenOnlineStore.save(/* @__PURE__ */ new Date());
25
- } else {
13
+ const shouldCheckDisconnectedUser = autologoutIsEnabled && enableEffect;
14
+ if (isLoggedIn === null) {
15
+ return;
16
+ }
17
+ if (!shouldCheckDisconnectedUser || !isLoggedIn) {
26
18
  lastSeenOnlineStore.delete();
19
+ return;
20
+ }
21
+ const lastSeenOnline = lastSeenOnlineStore.get();
22
+ if (lastSeenOnline) {
23
+ const now = /* @__PURE__ */ new Date();
24
+ const nowSeconds = Math.ceil(now.getTime() / 1e3);
25
+ const lastSeenOnlineSeconds = Math.ceil(lastSeenOnline.getTime() / 1e3);
26
+ if (nowSeconds - lastSeenOnlineSeconds > idleTimeoutSeconds) {
27
+ lastSeenOnlineStore.delete();
28
+ identityApi.signOut();
29
+ return;
30
+ }
27
31
  }
32
+ lastSeenOnlineStore.save(/* @__PURE__ */ new Date());
28
33
  }, [
29
34
  autologoutIsEnabled,
30
35
  enableEffect,
36
+ isLoggedIn,
31
37
  identityApi,
32
38
  idleTimeoutSeconds,
33
39
  lastSeenOnlineStore
@@ -1 +1 @@
1
- {"version":3,"file":"disconnectedUsers.esm.js","sources":["../../../src/components/AutoLogout/disconnectedUsers.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 */\nimport { IdentityApi } from '@backstage/core-plugin-api';\nimport { useEffect } from 'react';\n\nimport { TimestampStore } from './timestampStore';\n\nexport const LAST_SEEN_ONLINE_STORAGE_KEY =\n '@backstage/autologout:lastSeenOnline';\n\nexport type UseLogoutDisconnectedUserEffectProps = {\n enableEffect: boolean;\n autologoutIsEnabled: boolean;\n idleTimeoutSeconds: number;\n lastSeenOnlineStore: TimestampStore;\n identityApi: IdentityApi;\n};\n\nexport const useLogoutDisconnectedUserEffect = ({\n enableEffect,\n autologoutIsEnabled,\n idleTimeoutSeconds,\n lastSeenOnlineStore,\n identityApi,\n}: UseLogoutDisconnectedUserEffectProps) => {\n useEffect(() => {\n /**\n * Considers disconnected users as inactive users.\n * If all Backstage tabs are closed and idleTimeoutMinutes are passed then logout the user anyway.\n */\n if (autologoutIsEnabled && enableEffect) {\n const lastSeenOnline = lastSeenOnlineStore.get();\n if (lastSeenOnline) {\n const now = new Date();\n const nowSeconds = Math.ceil(now.getTime() / 1000);\n const lastSeenOnlineSeconds = Math.ceil(\n lastSeenOnline.getTime() / 1000,\n );\n if (nowSeconds - lastSeenOnlineSeconds > idleTimeoutSeconds) {\n identityApi.signOut();\n }\n }\n /**\n * save for the first time when app is loaded, so that\n * if user logs in and does nothing we still have a\n * lastSeenOnline value in store\n */\n lastSeenOnlineStore.save(new Date());\n } else {\n lastSeenOnlineStore.delete();\n }\n }, [\n autologoutIsEnabled,\n enableEffect,\n identityApi,\n idleTimeoutSeconds,\n lastSeenOnlineStore,\n ]);\n};\n"],"names":[],"mappings":";;AAoBO,MAAM,4BAAA,GACX;AAUK,MAAM,kCAAkC,CAAC;AAAA,EAC9C,YAAA;AAAA,EACA,mBAAA;AAAA,EACA,kBAAA;AAAA,EACA,mBAAA;AAAA,EACA;AACF,CAAA,KAA4C;AAC1C,EAAA,SAAA,CAAU,MAAM;AAKd,IAAA,IAAI,uBAAuB,YAAA,EAAc;AACvC,MAAA,MAAM,cAAA,GAAiB,oBAAoB,GAAA,EAAI;AAC/C,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,QAAA,MAAM,aAAa,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,OAAA,KAAY,GAAI,CAAA;AACjD,QAAA,MAAM,wBAAwB,IAAA,CAAK,IAAA;AAAA,UACjC,cAAA,CAAe,SAAQ,GAAI;AAAA,SAC7B;AACA,QAAA,IAAI,UAAA,GAAa,wBAAwB,kBAAA,EAAoB;AAC3D,UAAA,WAAA,CAAY,OAAA,EAAQ;AAAA,QACtB;AAAA,MACF;AAMA,MAAA,mBAAA,CAAoB,IAAA,iBAAK,IAAI,IAAA,EAAM,CAAA;AAAA,IACrC,CAAA,MAAO;AACL,MAAA,mBAAA,CAAoB,MAAA,EAAO;AAAA,IAC7B;AAAA,EACF,CAAA,EAAG;AAAA,IACD,mBAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA,kBAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"disconnectedUsers.esm.js","sources":["../../../src/components/AutoLogout/disconnectedUsers.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 */\nimport { IdentityApi } from '@backstage/core-plugin-api';\nimport { useEffect } from 'react';\n\nimport { TimestampStore } from './timestampStore';\n\nexport const LAST_SEEN_ONLINE_STORAGE_KEY =\n '@backstage/autologout:lastSeenOnline';\n\nexport type UseLogoutDisconnectedUserEffectProps = {\n enableEffect: boolean;\n autologoutIsEnabled: boolean;\n isLoggedIn: boolean | null;\n idleTimeoutSeconds: number;\n lastSeenOnlineStore: TimestampStore;\n identityApi: IdentityApi;\n};\n\nexport const useLogoutDisconnectedUserEffect = ({\n enableEffect,\n autologoutIsEnabled,\n isLoggedIn,\n idleTimeoutSeconds,\n lastSeenOnlineStore,\n identityApi,\n}: UseLogoutDisconnectedUserEffectProps) => {\n useEffect(() => {\n /**\n * Considers disconnected users as inactive users.\n * If all Backstage tabs are closed and idleTimeoutMinutes are passed then logout the user anyway.\n */\n const shouldCheckDisconnectedUser = autologoutIsEnabled && enableEffect;\n\n // Prevent lastSeen getting deleted before logged state is checked\n if (isLoggedIn === null) {\n return;\n }\n\n if (!shouldCheckDisconnectedUser || !isLoggedIn) {\n lastSeenOnlineStore.delete();\n return;\n }\n\n const lastSeenOnline = lastSeenOnlineStore.get();\n if (lastSeenOnline) {\n const now = new Date();\n const nowSeconds = Math.ceil(now.getTime() / 1000);\n const lastSeenOnlineSeconds = Math.ceil(lastSeenOnline.getTime() / 1000);\n if (nowSeconds - lastSeenOnlineSeconds > idleTimeoutSeconds) {\n lastSeenOnlineStore.delete();\n identityApi.signOut();\n return;\n }\n }\n lastSeenOnlineStore.save(new Date());\n }, [\n autologoutIsEnabled,\n enableEffect,\n isLoggedIn,\n identityApi,\n idleTimeoutSeconds,\n lastSeenOnlineStore,\n ]);\n};\n"],"names":[],"mappings":";;AAoBO,MAAM,4BAAA,GACX;AAWK,MAAM,kCAAkC,CAAC;AAAA,EAC9C,YAAA;AAAA,EACA,mBAAA;AAAA,EACA,UAAA;AAAA,EACA,kBAAA;AAAA,EACA,mBAAA;AAAA,EACA;AACF,CAAA,KAA4C;AAC1C,EAAA,SAAA,CAAU,MAAM;AAKd,IAAA,MAAM,8BAA8B,mBAAA,IAAuB,YAAA;AAG3D,IAAA,IAAI,eAAe,IAAA,EAAM;AACvB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,2BAAA,IAA+B,CAAC,UAAA,EAAY;AAC/C,MAAA,mBAAA,CAAoB,MAAA,EAAO;AAC3B,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,cAAA,GAAiB,oBAAoB,GAAA,EAAI;AAC/C,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,MAAA,MAAM,aAAa,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,OAAA,KAAY,GAAI,CAAA;AACjD,MAAA,MAAM,wBAAwB,IAAA,CAAK,IAAA,CAAK,cAAA,CAAe,OAAA,KAAY,GAAI,CAAA;AACvE,MAAA,IAAI,UAAA,GAAa,wBAAwB,kBAAA,EAAoB;AAC3D,QAAA,mBAAA,CAAoB,MAAA,EAAO;AAC3B,QAAA,WAAA,CAAY,OAAA,EAAQ;AACpB,QAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,mBAAA,CAAoB,IAAA,iBAAK,IAAI,IAAA,EAAM,CAAA;AAAA,EACrC,CAAA,EAAG;AAAA,IACD,mBAAA;AAAA,IACA,YAAA;AAAA,IACA,UAAA;AAAA,IACA,WAAA;AAAA,IACA,kBAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;;;;"}
@@ -1,45 +1,11 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
2
- import Box from '@material-ui/core/Box';
3
- import { useTheme } from '@material-ui/core/styles';
4
- import LightAsync from 'react-syntax-highlighter/dist/esm/light-async';
5
- import dark from 'react-syntax-highlighter/dist/esm/styles/hljs/dark';
6
- import docco from 'react-syntax-highlighter/dist/esm/styles/hljs/docco';
7
- import { CopyTextButton } from '../CopyTextButton/CopyTextButton.esm.js';
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { lazy, Suspense } from 'react';
8
3
 
4
+ const LazyCodeSnippetContent = lazy(
5
+ () => import('./CodeSnippetContent.esm.js').then((m) => ({ default: m.CodeSnippet }))
6
+ );
9
7
  function CodeSnippet(props) {
10
- const {
11
- text,
12
- language,
13
- showLineNumbers = false,
14
- highlightedNumbers,
15
- wrapLongLines,
16
- customStyle,
17
- showCopyCodeButton = false
18
- } = props;
19
- const theme = useTheme();
20
- const mode = theme.palette.type === "dark" ? dark : docco;
21
- const highlightColor = theme.palette.type === "dark" ? "#256bf3" : "#e6ffed";
22
- return /* @__PURE__ */ jsxs(Box, { position: "relative", children: [
23
- /* @__PURE__ */ jsx(
24
- LightAsync,
25
- {
26
- customStyle,
27
- language,
28
- style: mode,
29
- showLineNumbers,
30
- wrapLines: true,
31
- wrapLongLines,
32
- lineNumberStyle: { color: theme.palette.textVerySubtle },
33
- lineProps: (lineNumber) => highlightedNumbers?.includes(lineNumber) ? {
34
- style: {
35
- backgroundColor: highlightColor
36
- }
37
- } : {},
38
- children: text
39
- }
40
- ),
41
- showCopyCodeButton && /* @__PURE__ */ jsx(Box, { position: "absolute", top: 0, right: 0, children: /* @__PURE__ */ jsx(CopyTextButton, { text }) })
42
- ] });
8
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", {}), children: /* @__PURE__ */ jsx(LazyCodeSnippetContent, { ...props }) });
43
9
  }
44
10
 
45
11
  export { CodeSnippet };
@@ -1 +1 @@
1
- {"version":3,"file":"CodeSnippet.esm.js","sources":["../../../src/components/CodeSnippet/CodeSnippet.tsx"],"sourcesContent":["/*\n * Copyright 2020 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 Box from '@material-ui/core/Box';\nimport { useTheme } from '@material-ui/core/styles';\nimport type {} from 'react-syntax-highlighter';\nimport LightAsync from 'react-syntax-highlighter/dist/esm/light-async';\nimport dark from 'react-syntax-highlighter/dist/esm/styles/hljs/dark';\nimport docco from 'react-syntax-highlighter/dist/esm/styles/hljs/docco';\n\nimport { CopyTextButton } from '../CopyTextButton';\n\n/**\n * Properties for {@link CodeSnippet}\n *\n * @public\n */\nexport interface CodeSnippetProps {\n /**\n * Code Snippet text\n */\n text: string;\n /**\n * Language used by {@link CodeSnippetProps.text}\n */\n language: string;\n /**\n * Whether to show line number\n *\n * @remarks\n *\n * Default: false\n */\n showLineNumbers?: boolean;\n /**\n * Whether to show button to copy code snippet\n *\n * @remarks\n *\n * Default: false\n */\n showCopyCodeButton?: boolean;\n /**\n * Array of line numbers to highlight\n */\n highlightedNumbers?: number[];\n /**\n * Whether to style the `<code>` block with `white-space: pre-wrap` or `white-space: pre`\n *\n * @remarks\n *\n * Default: false (`white-space: pre`)\n */\n wrapLongLines?: boolean;\n /**\n * Custom styles applied to code\n *\n * @remarks\n *\n * Passed to {@link https://react-syntax-highlighter.github.io/react-syntax-highlighter/ | react-syntax-highlighter}\n */\n customStyle?: any;\n}\n\n/**\n * Thin wrapper on top of {@link https://react-syntax-highlighter.github.io/react-syntax-highlighter/ | react-syntax-highlighter}\n * providing consistent theming and copy code button\n *\n * @public\n */\nexport function CodeSnippet(props: CodeSnippetProps) {\n const {\n text,\n language,\n showLineNumbers = false,\n highlightedNumbers,\n wrapLongLines,\n customStyle,\n showCopyCodeButton = false,\n } = props;\n const theme = useTheme();\n const mode = theme.palette.type === 'dark' ? dark : docco;\n const highlightColor = theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed';\n\n return (\n <Box position=\"relative\">\n <LightAsync\n customStyle={customStyle}\n language={language}\n style={mode}\n showLineNumbers={showLineNumbers}\n wrapLines\n wrapLongLines={wrapLongLines}\n lineNumberStyle={{ color: theme.palette.textVerySubtle }}\n lineProps={(lineNumber: number) =>\n highlightedNumbers?.includes(lineNumber)\n ? {\n style: {\n backgroundColor: highlightColor,\n },\n }\n : {}\n }\n >\n {text}\n </LightAsync>\n {showCopyCodeButton && (\n <Box position=\"absolute\" top={0} right={0}>\n <CopyTextButton text={text} />\n </Box>\n )}\n </Box>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAmFO,SAAS,YAAY,KAAA,EAAyB;AACnD,EAAA,MAAM;AAAA,IACJ,IAAA;AAAA,IACA,QAAA;AAAA,IACA,eAAA,GAAkB,KAAA;AAAA,IAClB,kBAAA;AAAA,IACA,aAAA;AAAA,IACA,WAAA;AAAA,IACA,kBAAA,GAAqB;AAAA,GACvB,GAAI,KAAA;AACJ,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,SAAS,IAAA,GAAO,KAAA;AACpD,EAAA,MAAM,cAAA,GAAiB,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,SAAS,SAAA,GAAY,SAAA;AAEnE,EAAA,uBACE,IAAA,CAAC,GAAA,EAAA,EAAI,QAAA,EAAS,UAAA,EACZ,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,WAAA;AAAA,QACA,QAAA;AAAA,QACA,KAAA,EAAO,IAAA;AAAA,QACP,eAAA;AAAA,QACA,SAAA,EAAS,IAAA;AAAA,QACT,aAAA;AAAA,QACA,eAAA,EAAiB,EAAE,KAAA,EAAO,KAAA,CAAM,QAAQ,cAAA,EAAe;AAAA,QACvD,WAAW,CAAC,UAAA,KACV,kBAAA,EAAoB,QAAA,CAAS,UAAU,CAAA,GACnC;AAAA,UACE,KAAA,EAAO;AAAA,YACL,eAAA,EAAiB;AAAA;AACnB,YAEF,EAAC;AAAA,QAGN,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,IACC,kBAAA,oBACC,GAAA,CAAC,GAAA,EAAA,EAAI,QAAA,EAAS,UAAA,EAAW,GAAA,EAAK,CAAA,EAAG,KAAA,EAAO,CAAA,EACtC,QAAA,kBAAA,GAAA,CAAC,cAAA,EAAA,EAAe,IAAA,EAAY,CAAA,EAC9B;AAAA,GAAA,EAEJ,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"CodeSnippet.esm.js","sources":["../../../src/components/CodeSnippet/CodeSnippet.tsx"],"sourcesContent":["/*\n * Copyright 2020 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 { Suspense, lazy } from 'react';\nimport type { CodeSnippetProps } from './CodeSnippetContent';\n\nexport type { CodeSnippetProps } from './CodeSnippetContent';\n\nconst LazyCodeSnippetContent = lazy(() =>\n import('./CodeSnippetContent').then(m => ({ default: m.CodeSnippet })),\n);\n\n/**\n * Thin wrapper on top of {@link https://react-syntax-highlighter.github.io/react-syntax-highlighter/ | react-syntax-highlighter}\n * providing consistent theming and copy code button\n *\n * @public\n */\nexport function CodeSnippet(props: CodeSnippetProps) {\n return (\n <Suspense fallback={<div />}>\n <LazyCodeSnippetContent {...props} />\n </Suspense>\n );\n}\n"],"names":[],"mappings":";;;AAqBA,MAAM,sBAAA,GAAyB,IAAA;AAAA,EAAK,MAClC,OAAO,6BAAsB,CAAA,CAAE,IAAA,CAAK,QAAM,EAAE,OAAA,EAAS,CAAA,CAAE,WAAA,EAAY,CAAE;AACvE,CAAA;AAQO,SAAS,YAAY,KAAA,EAAyB;AACnD,EAAA,uBACE,GAAA,CAAC,QAAA,EAAA,EAAS,QAAA,kBAAU,GAAA,CAAC,KAAA,EAAA,EAAI,GACvB,QAAA,kBAAA,GAAA,CAAC,sBAAA,EAAA,EAAwB,GAAG,KAAA,EAAO,CAAA,EACrC,CAAA;AAEJ;;;;"}
@@ -0,0 +1,46 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import Box from '@material-ui/core/Box';
3
+ import { useTheme } from '@material-ui/core/styles';
4
+ import LightAsync from 'react-syntax-highlighter/dist/esm/light-async';
5
+ import dark from 'react-syntax-highlighter/dist/esm/styles/hljs/dark';
6
+ import docco from 'react-syntax-highlighter/dist/esm/styles/hljs/docco';
7
+ import { CopyTextButton } from '../CopyTextButton/CopyTextButton.esm.js';
8
+
9
+ function CodeSnippet(props) {
10
+ const {
11
+ text,
12
+ language,
13
+ showLineNumbers = false,
14
+ highlightedNumbers,
15
+ wrapLongLines,
16
+ customStyle,
17
+ showCopyCodeButton = false
18
+ } = props;
19
+ const theme = useTheme();
20
+ const mode = theme.palette.type === "dark" ? dark : docco;
21
+ const highlightColor = theme.palette.type === "dark" ? "#256bf3" : "#e6ffed";
22
+ return /* @__PURE__ */ jsxs(Box, { position: "relative", children: [
23
+ /* @__PURE__ */ jsx(
24
+ LightAsync,
25
+ {
26
+ customStyle,
27
+ language,
28
+ style: mode,
29
+ showLineNumbers,
30
+ wrapLines: true,
31
+ wrapLongLines,
32
+ lineNumberStyle: { color: theme.palette.textVerySubtle },
33
+ lineProps: (lineNumber) => highlightedNumbers?.includes(lineNumber) ? {
34
+ style: {
35
+ backgroundColor: highlightColor
36
+ }
37
+ } : {},
38
+ children: text
39
+ }
40
+ ),
41
+ showCopyCodeButton && /* @__PURE__ */ jsx(Box, { position: "absolute", top: 0, right: 0, children: /* @__PURE__ */ jsx(CopyTextButton, { text }) })
42
+ ] });
43
+ }
44
+
45
+ export { CodeSnippet };
46
+ //# sourceMappingURL=CodeSnippetContent.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeSnippetContent.esm.js","sources":["../../../src/components/CodeSnippet/CodeSnippetContent.tsx"],"sourcesContent":["/*\n * Copyright 2020 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 Box from '@material-ui/core/Box';\nimport { useTheme } from '@material-ui/core/styles';\nimport type {} from 'react-syntax-highlighter';\nimport LightAsync from 'react-syntax-highlighter/dist/esm/light-async';\nimport dark from 'react-syntax-highlighter/dist/esm/styles/hljs/dark';\nimport docco from 'react-syntax-highlighter/dist/esm/styles/hljs/docco';\n\nimport { CopyTextButton } from '../CopyTextButton';\n\n/**\n * Properties for {@link CodeSnippet}\n *\n * @public\n */\nexport interface CodeSnippetProps {\n /**\n * Code Snippet text\n */\n text: string;\n /**\n * Language used by {@link CodeSnippetProps.text}\n */\n language: string;\n /**\n * Whether to show line number\n *\n * @remarks\n *\n * Default: false\n */\n showLineNumbers?: boolean;\n /**\n * Whether to show button to copy code snippet\n *\n * @remarks\n *\n * Default: false\n */\n showCopyCodeButton?: boolean;\n /**\n * Array of line numbers to highlight\n */\n highlightedNumbers?: number[];\n /**\n * Whether to style the `<code>` block with `white-space: pre-wrap` or `white-space: pre`\n *\n * @remarks\n *\n * Default: false (`white-space: pre`)\n */\n wrapLongLines?: boolean;\n /**\n * Custom styles applied to code\n *\n * @remarks\n *\n * Passed to {@link https://react-syntax-highlighter.github.io/react-syntax-highlighter/ | react-syntax-highlighter}\n */\n customStyle?: any;\n}\n\n/**\n * Thin wrapper on top of {@link https://react-syntax-highlighter.github.io/react-syntax-highlighter/ | react-syntax-highlighter}\n * providing consistent theming and copy code button\n *\n * @public\n */\nexport function CodeSnippet(props: CodeSnippetProps) {\n const {\n text,\n language,\n showLineNumbers = false,\n highlightedNumbers,\n wrapLongLines,\n customStyle,\n showCopyCodeButton = false,\n } = props;\n const theme = useTheme();\n const mode = theme.palette.type === 'dark' ? dark : docco;\n const highlightColor = theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed';\n\n return (\n <Box position=\"relative\">\n <LightAsync\n customStyle={customStyle}\n language={language}\n style={mode}\n showLineNumbers={showLineNumbers}\n wrapLines\n wrapLongLines={wrapLongLines}\n lineNumberStyle={{ color: theme.palette.textVerySubtle }}\n lineProps={(lineNumber: number) =>\n highlightedNumbers?.includes(lineNumber)\n ? {\n style: {\n backgroundColor: highlightColor,\n },\n }\n : {}\n }\n >\n {text}\n </LightAsync>\n {showCopyCodeButton && (\n <Box position=\"absolute\" top={0} right={0}>\n <CopyTextButton text={text} />\n </Box>\n )}\n </Box>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAmFO,SAAS,YAAY,KAAA,EAAyB;AACnD,EAAA,MAAM;AAAA,IACJ,IAAA;AAAA,IACA,QAAA;AAAA,IACA,eAAA,GAAkB,KAAA;AAAA,IAClB,kBAAA;AAAA,IACA,aAAA;AAAA,IACA,WAAA;AAAA,IACA,kBAAA,GAAqB;AAAA,GACvB,GAAI,KAAA;AACJ,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,SAAS,IAAA,GAAO,KAAA;AACpD,EAAA,MAAM,cAAA,GAAiB,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,SAAS,SAAA,GAAY,SAAA;AAEnE,EAAA,uBACE,IAAA,CAAC,GAAA,EAAA,EAAI,QAAA,EAAS,UAAA,EACZ,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,WAAA;AAAA,QACA,QAAA;AAAA,QACA,KAAA,EAAO,IAAA;AAAA,QACP,eAAA;AAAA,QACA,SAAA,EAAS,IAAA;AAAA,QACT,aAAA;AAAA,QACA,eAAA,EAAiB,EAAE,KAAA,EAAO,KAAA,CAAM,QAAQ,cAAA,EAAe;AAAA,QACvD,WAAW,CAAC,UAAA,KACV,kBAAA,EAAoB,QAAA,CAAS,UAAU,CAAA,GACnC;AAAA,UACE,KAAA,EAAO;AAAA,YACL,eAAA,EAAiB;AAAA;AACnB,YAEF,EAAC;AAAA,QAGN,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,IACC,kBAAA,oBACC,GAAA,CAAC,GAAA,EAAA,EAAI,QAAA,EAAS,UAAA,EAAW,GAAA,EAAK,CAAA,EAAG,KAAA,EAAO,CAAA,EACtC,QAAA,kBAAA,GAAA,CAAC,cAAA,EAAA,EAAe,IAAA,EAAY,CAAA,EAC9B;AAAA,GAAA,EAEJ,CAAA;AAEJ;;;;"}
@@ -1,341 +1,13 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { useState, useRef, useMemo, useCallback, useEffect } from 'react';
3
- import useMeasure from 'react-use/esm/useMeasure';
4
- import classNames from 'classnames';
5
- import { once } from 'lodash';
6
- import * as d3Zoom from 'd3-zoom';
7
- import * as d3Selection from 'd3-selection';
8
- import useTheme from '@material-ui/core/styles/useTheme';
9
- import dagre from '@dagrejs/dagre';
10
- import debounce from 'lodash/debounce';
11
- import { DependencyGraphTypes } from './types.esm.js';
12
- import { Node } from './Node.esm.js';
13
- import { Edge } from './Edge.esm.js';
14
- import { ARROW_MARKER_ID } from './constants.esm.js';
15
- import IconButton from '@material-ui/core/IconButton';
16
- import FullscreenIcon from '@material-ui/icons/Fullscreen';
17
- import FullscreenExitIcon from '@material-ui/icons/FullscreenExit';
18
- import { useFullScreenHandle, FullScreen } from 'react-full-screen';
19
- import { makeStyles } from '@material-ui/core/styles';
20
- import Tooltip from '@material-ui/core/Tooltip';
21
- import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
22
- import { coreComponentsTranslationRef } from '../../translation.esm.js';
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { lazy, Suspense } from 'react';
23
3
 
24
- const useStyles = makeStyles((theme) => ({
25
- fullscreenButton: {
26
- position: "absolute",
27
- right: 0
28
- },
29
- root: {
30
- overflow: "hidden",
31
- minHeight: "100%",
32
- minWidth: "100%"
33
- },
34
- fixedHeight: {
35
- maxHeight: "100%"
36
- },
37
- fullscreen: {
38
- backgroundColor: theme.palette.background.paper
39
- }
40
- }));
41
- const WORKSPACE_ID = "workspace";
42
- const DEPENDENCY_GRAPH_SVG = "dependency-graph";
4
+ const LazyDependencyGraphContent = lazy(
5
+ () => import('./DependencyGraphContent.esm.js').then((m) => ({
6
+ default: m.DependencyGraph
7
+ }))
8
+ );
43
9
  function DependencyGraph(props) {
44
- const {
45
- edges,
46
- nodes,
47
- renderNode,
48
- direction = DependencyGraphTypes.Direction.TOP_BOTTOM,
49
- align,
50
- nodeMargin = 50,
51
- edgeMargin = 10,
52
- rankMargin = 50,
53
- paddingX = 0,
54
- paddingY = 0,
55
- acyclicer,
56
- ranker = DependencyGraphTypes.Ranker.NETWORK_SIMPLEX,
57
- labelPosition = DependencyGraphTypes.LabelPosition.RIGHT,
58
- labelOffset = 10,
59
- edgeRanks = 1,
60
- edgeWeight = 1,
61
- renderEdge,
62
- renderLabel,
63
- defs,
64
- zoom = "enabled",
65
- curve = "curveMonotoneX",
66
- showArrowHeads = false,
67
- fit = "grow",
68
- allowFullscreen = true,
69
- ...svgProps
70
- } = props;
71
- const theme = useTheme();
72
- const [containerWidth, setContainerWidth] = useState(100);
73
- const [containerHeight, setContainerHeight] = useState(100);
74
- const fullScreenHandle = useFullScreenHandle();
75
- const styles = useStyles();
76
- const { t } = useTranslationRef(coreComponentsTranslationRef);
77
- const graph = useRef(
78
- new dagre.graphlib.Graph()
79
- );
80
- const [graphWidth, setGraphWidth] = useState(
81
- graph.current.graph()?.width || 0
82
- );
83
- const [graphHeight, setGraphHeight] = useState(
84
- graph.current.graph()?.height || 0
85
- );
86
- const [graphNodes, setGraphNodes] = useState([]);
87
- const [graphEdges, setGraphEdges] = useState([]);
88
- const maxWidth = Math.max(graphWidth, containerWidth);
89
- const maxHeight = Math.max(graphHeight, containerHeight);
90
- const [_measureRef] = useMeasure();
91
- const measureRef = once(_measureRef);
92
- const scalableHeight = fit === "grow" && !fullScreenHandle.active ? maxHeight : "100%";
93
- const containerRef = useMemo(
94
- () => debounce((root) => {
95
- if (!root) {
96
- return;
97
- }
98
- measureRef(root);
99
- const node = root.querySelector(
100
- `svg#${DEPENDENCY_GRAPH_SVG}`
101
- );
102
- if (!node) {
103
- return;
104
- }
105
- const container = d3Selection.select(node);
106
- const workspace = d3Selection.select(node.getElementById(WORKSPACE_ID));
107
- function enableZoom() {
108
- container.call(
109
- d3Zoom.zoom().scaleExtent([1, Infinity]).on("zoom", (event) => {
110
- event.transform.x = Math.min(
111
- 0,
112
- Math.max(
113
- event.transform.x,
114
- maxWidth - maxWidth * event.transform.k
115
- )
116
- );
117
- event.transform.y = Math.min(
118
- 0,
119
- Math.max(
120
- event.transform.y,
121
- maxHeight - maxHeight * event.transform.k
122
- )
123
- );
124
- workspace.attr("transform", event.transform);
125
- })
126
- );
127
- }
128
- if (zoom === "enabled") {
129
- enableZoom();
130
- } else if (zoom === "enable-on-click") {
131
- container.on("click", () => enableZoom());
132
- }
133
- const { width: newContainerWidth, height: newContainerHeight } = root.getBoundingClientRect();
134
- if (containerWidth !== newContainerWidth && newContainerWidth <= maxWidth) {
135
- setContainerWidth(newContainerWidth);
136
- }
137
- if (containerHeight !== newContainerHeight && newContainerHeight <= maxHeight) {
138
- setContainerHeight(newContainerHeight);
139
- }
140
- }, 100),
141
- [measureRef, containerHeight, containerWidth, maxWidth, maxHeight, zoom]
142
- );
143
- const setNodesAndEdges = useCallback(() => {
144
- const currentGraphNodes = graph.current.nodes();
145
- const currentGraphEdges = graph.current.edges();
146
- currentGraphNodes.forEach((nodeId) => {
147
- const remainingNode = nodes.some((node) => node.id === nodeId);
148
- if (!remainingNode) {
149
- graph.current.removeNode(nodeId);
150
- }
151
- });
152
- currentGraphEdges.forEach((e) => {
153
- const remainingEdge = edges.some(
154
- (edge) => edge.from === e.v && edge.to === e.w
155
- );
156
- if (!remainingEdge) {
157
- graph.current.removeEdge(e.v, e.w);
158
- }
159
- });
160
- nodes.forEach((node) => {
161
- const existingNode = graph.current.nodes().find((nodeId) => node.id === nodeId);
162
- if (existingNode && graph.current.node(existingNode)) {
163
- const { width, height, x, y } = graph.current.node(existingNode);
164
- graph.current.setNode(existingNode, { ...node, width, height, x, y });
165
- } else {
166
- graph.current.setNode(node.id, { ...node, width: 0, height: 0 });
167
- }
168
- });
169
- edges.forEach((e) => {
170
- graph.current.setEdge(e.from, e.to, {
171
- ...e,
172
- label: e.label,
173
- width: 0,
174
- height: 0,
175
- labelpos: labelPosition,
176
- labeloffset: labelOffset,
177
- weight: edgeWeight,
178
- minlen: edgeRanks
179
- });
180
- });
181
- }, [edges, nodes, labelPosition, labelOffset, edgeWeight, edgeRanks]);
182
- const updateGraph = useMemo(
183
- () => debounce(
184
- () => {
185
- dagre.layout(graph.current);
186
- const { height, width } = graph.current.graph();
187
- const newHeight = Math.max(0, height || 0);
188
- const newWidth = Math.max(0, width || 0);
189
- setGraphWidth(newWidth);
190
- setGraphHeight(newHeight);
191
- setGraphNodes(graph.current.nodes());
192
- setGraphEdges(graph.current.edges());
193
- },
194
- 250,
195
- { leading: true }
196
- ),
197
- []
198
- );
199
- useEffect(() => {
200
- graph.current.setGraph({
201
- rankdir: direction,
202
- align,
203
- nodesep: nodeMargin,
204
- edgesep: edgeMargin,
205
- ranksep: rankMargin,
206
- marginx: paddingX,
207
- marginy: paddingY,
208
- acyclicer,
209
- ranker
210
- });
211
- setNodesAndEdges();
212
- updateGraph();
213
- return updateGraph.cancel;
214
- }, [
215
- acyclicer,
216
- align,
217
- direction,
218
- edgeMargin,
219
- paddingX,
220
- paddingY,
221
- nodeMargin,
222
- rankMargin,
223
- ranker,
224
- setNodesAndEdges,
225
- updateGraph
226
- ]);
227
- const setNode = useCallback(
228
- (id, node) => {
229
- graph.current.setNode(id, node);
230
- updateGraph();
231
- return graph.current;
232
- },
233
- [updateGraph]
234
- );
235
- const setEdge = useCallback(
236
- (id, edge) => {
237
- graph.current.setEdge(id, edge);
238
- updateGraph();
239
- return graph.current;
240
- },
241
- [updateGraph]
242
- );
243
- return /* @__PURE__ */ jsxs(
244
- FullScreen,
245
- {
246
- handle: fullScreenHandle,
247
- className: classNames(
248
- fullScreenHandle.active ? styles.fullscreen : styles.root
249
- ),
250
- children: [
251
- allowFullscreen && /* @__PURE__ */ jsx(Tooltip, { title: t("dependencyGraph.fullscreenTooltip"), children: /* @__PURE__ */ jsx(
252
- IconButton,
253
- {
254
- className: styles.fullscreenButton,
255
- onClick: fullScreenHandle.active ? fullScreenHandle.exit : fullScreenHandle.enter,
256
- children: fullScreenHandle.active ? /* @__PURE__ */ jsx(FullscreenExitIcon, {}) : /* @__PURE__ */ jsx(FullscreenIcon, {})
257
- }
258
- ) }),
259
- /* @__PURE__ */ jsx("div", { ref: containerRef, style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxs(
260
- "svg",
261
- {
262
- ...svgProps,
263
- width: "100%",
264
- height: scalableHeight,
265
- viewBox: `0 0 ${maxWidth} ${maxHeight}`,
266
- id: DEPENDENCY_GRAPH_SVG,
267
- children: [
268
- /* @__PURE__ */ jsxs("defs", { children: [
269
- /* @__PURE__ */ jsx(
270
- "marker",
271
- {
272
- id: ARROW_MARKER_ID,
273
- viewBox: "0 0 24 24",
274
- markerWidth: "14",
275
- markerHeight: "14",
276
- refX: "16",
277
- refY: "12",
278
- orient: "auto",
279
- markerUnits: "strokeWidth",
280
- children: /* @__PURE__ */ jsx(
281
- "path",
282
- {
283
- fill: theme.palette.textSubtle,
284
- d: "M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"
285
- }
286
- )
287
- }
288
- ),
289
- defs
290
- ] }),
291
- /* @__PURE__ */ jsx("g", { id: WORKSPACE_ID, children: /* @__PURE__ */ jsxs(
292
- "svg",
293
- {
294
- width: graphWidth,
295
- height: graphHeight,
296
- y: maxHeight / 2 - graphHeight / 2,
297
- x: maxWidth / 2 - graphWidth / 2,
298
- viewBox: `0 0 ${graphWidth} ${graphHeight}`,
299
- children: [
300
- graphEdges.map((e) => {
301
- const edge = graph.current.edge(e);
302
- if (!edge) return null;
303
- if (renderEdge) return renderEdge({ edge, id: e });
304
- return /* @__PURE__ */ jsx(
305
- Edge,
306
- {
307
- id: e,
308
- setEdge,
309
- render: renderLabel,
310
- edge,
311
- curve,
312
- showArrowHeads
313
- },
314
- `${e.v}-${e.w}`
315
- );
316
- }),
317
- graphNodes.map((id) => {
318
- const node = graph.current.node(id);
319
- if (!node) return null;
320
- return /* @__PURE__ */ jsx(
321
- Node,
322
- {
323
- setNode,
324
- render: renderNode,
325
- node
326
- },
327
- id
328
- );
329
- })
330
- ]
331
- }
332
- ) })
333
- ]
334
- }
335
- ) })
336
- ]
337
- }
338
- );
10
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", {}), children: /* @__PURE__ */ jsx(LazyDependencyGraphContent, { ...props }) });
339
11
  }
340
12
 
341
13
  export { DependencyGraph };
@@ -1 +1 @@
1
- {"version":3,"file":"DependencyGraph.esm.js","sources":["../../../src/components/DependencyGraph/DependencyGraph.tsx"],"sourcesContent":["/*\n * Copyright 2020 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 SVGProps,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport useMeasure from 'react-use/esm/useMeasure';\nimport classNames from 'classnames';\nimport { once } from 'lodash';\nimport * as d3Zoom from 'd3-zoom';\nimport * as d3Selection from 'd3-selection';\nimport useTheme from '@material-ui/core/styles/useTheme';\nimport dagre from '@dagrejs/dagre';\nimport debounce from 'lodash/debounce';\nimport { DependencyGraphTypes as Types } from './types';\nimport { Node } from './Node';\nimport { Edge, GraphEdge } from './Edge';\nimport { ARROW_MARKER_ID } from './constants';\nimport IconButton from '@material-ui/core/IconButton';\nimport FullscreenIcon from '@material-ui/icons/Fullscreen';\nimport FullscreenExitIcon from '@material-ui/icons/FullscreenExit';\nimport { FullScreen, useFullScreenHandle } from 'react-full-screen';\nimport { makeStyles, Theme } from '@material-ui/core/styles';\nimport Tooltip from '@material-ui/core/Tooltip';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { coreComponentsTranslationRef } from '../../translation';\n\nconst useStyles = makeStyles((theme: Theme) => ({\n fullscreenButton: {\n position: 'absolute',\n right: 0,\n },\n root: {\n overflow: 'hidden',\n minHeight: '100%',\n minWidth: '100%',\n },\n fixedHeight: {\n maxHeight: '100%',\n },\n fullscreen: {\n backgroundColor: theme.palette.background.paper,\n },\n}));\n\n/**\n * Properties of {@link DependencyGraph}\n *\n * @public\n * @remarks\n * `<NodeData>` and `<EdgeData>` are useful when rendering custom or edge labels\n */\nexport interface DependencyGraphProps<NodeData, EdgeData>\n extends SVGProps<SVGSVGElement> {\n /**\n * Edges of graph\n */\n edges: Types.DependencyEdge<EdgeData>[];\n /**\n * Nodes of Graph\n */\n nodes: Types.DependencyNode<NodeData>[];\n /**\n * Graph {@link DependencyGraphTypes.(Direction:namespace) | direction}\n *\n * @remarks\n *\n * Default: `DependencyGraphTypes.Direction.TOP_BOTTOM`\n */\n direction?: Types.Direction;\n /**\n * Node {@link DependencyGraphTypes.(Alignment:namespace) | alignment}\n */\n align?: Types.Alignment;\n /**\n * Margin between nodes on each rank\n *\n * @remarks\n *\n * Default: 50\n */\n nodeMargin?: number;\n /**\n * Margin between edges\n *\n * @remarks\n *\n * Default: 10\n */\n edgeMargin?: number;\n /**\n * Margin between each rank\n *\n * @remarks\n *\n * Default: 50\n */\n rankMargin?: number;\n /**\n * Margin on left and right of whole graph\n *\n * @remarks\n *\n * Default: 0\n */\n paddingX?: number;\n /**\n * Margin on top and bottom of whole graph\n *\n * @remarks\n *\n * Default: 0\n */\n paddingY?: number;\n /**\n * Heuristic used to find set of edges that will make graph acyclic\n */\n acyclicer?: 'greedy';\n /**\n * {@link DependencyGraphTypes.(Ranker:namespace) | Algorithm} used to rank nodes\n *\n * @remarks\n *\n * Default: `DependencyGraphTypes.Ranker.NETWORK_SIMPLEX`\n */\n ranker?: Types.Ranker;\n /**\n * {@link DependencyGraphTypes.(LabelPosition:namespace) | Position} of label in relation to edge\n *\n * @remarks\n *\n * Default: `DependencyGraphTypes.LabelPosition.RIGHT`\n */\n labelPosition?: Types.LabelPosition;\n /**\n * How much to move label away from edge\n *\n * @remarks\n *\n * Applies only when {@link DependencyGraphProps.labelPosition} is `DependencyGraphTypes.LabelPosition.LEFT` or\n * `DependencyGraphTypes.LabelPosition.RIGHT`\n */\n labelOffset?: number;\n /**\n * Minimum number of ranks to keep between connected nodes\n */\n edgeRanks?: number;\n /**\n * Weight applied to edges in graph\n */\n edgeWeight?: number;\n /**\n * Custom edge rendering component\n */\n renderEdge?: Types.RenderEdgeFunction<EdgeData>;\n /**\n * Custom node rendering component\n */\n renderNode?: Types.RenderNodeFunction<NodeData>;\n /**\n * Custom label rendering component\n */\n renderLabel?: Types.RenderLabelFunction<EdgeData>;\n /**\n * {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs | Defs} shared by rendered SVG to be used by\n * {@link DependencyGraphProps.renderNode} and/or {@link DependencyGraphProps.renderLabel}\n */\n defs?: JSX.Element | JSX.Element[];\n /**\n * Controls zoom behavior of graph\n *\n * @remarks\n *\n * Default: `enabled`\n */\n zoom?: 'enabled' | 'disabled' | 'enable-on-click';\n /**\n * A factory for curve generators addressing both lines and areas.\n *\n * @remarks\n *\n * Default: 'curveMonotoneX'\n */\n curve?: 'curveStepBefore' | 'curveMonotoneX';\n /**\n * Controls if the arrow heads should be rendered or not.\n *\n * Default: false\n */\n showArrowHeads?: boolean;\n /**\n * Controls if the graph should be contained or grow\n *\n * @remarks\n *\n * Default: 'grow'\n */\n fit?: 'grow' | 'contain';\n /**\n * Controls if user can toggle fullscreen mode\n *\n * @remarks\n *\n * Default: true\n */\n allowFullscreen?: boolean;\n}\n\nconst WORKSPACE_ID = 'workspace';\nconst DEPENDENCY_GRAPH_SVG = 'dependency-graph';\n\n/**\n * Graph component used to visualize relations between entities\n *\n * @public\n */\nexport function DependencyGraph<NodeData, EdgeData>(\n props: DependencyGraphProps<NodeData, EdgeData>,\n) {\n const {\n edges,\n nodes,\n renderNode,\n direction = Types.Direction.TOP_BOTTOM,\n align,\n nodeMargin = 50,\n edgeMargin = 10,\n rankMargin = 50,\n paddingX = 0,\n paddingY = 0,\n acyclicer,\n ranker = Types.Ranker.NETWORK_SIMPLEX,\n labelPosition = Types.LabelPosition.RIGHT,\n labelOffset = 10,\n edgeRanks = 1,\n edgeWeight = 1,\n renderEdge,\n renderLabel,\n defs,\n zoom = 'enabled',\n curve = 'curveMonotoneX',\n showArrowHeads = false,\n fit = 'grow',\n allowFullscreen = true,\n ...svgProps\n } = props;\n const theme = useTheme();\n const [containerWidth, setContainerWidth] = useState<number>(100);\n const [containerHeight, setContainerHeight] = useState<number>(100);\n const fullScreenHandle = useFullScreenHandle();\n const styles = useStyles();\n const { t } = useTranslationRef(coreComponentsTranslationRef);\n\n const graph = useRef<dagre.graphlib.Graph<Types.DependencyNode<NodeData>>>(\n new dagre.graphlib.Graph(),\n );\n const [graphWidth, setGraphWidth] = useState<number>(\n graph.current.graph()?.width || 0,\n );\n const [graphHeight, setGraphHeight] = useState<number>(\n graph.current.graph()?.height || 0,\n );\n const [graphNodes, setGraphNodes] = useState<string[]>([]);\n const [graphEdges, setGraphEdges] = useState<dagre.Edge[]>([]);\n\n const maxWidth = Math.max(graphWidth, containerWidth);\n const maxHeight = Math.max(graphHeight, containerHeight);\n\n const [_measureRef] = useMeasure();\n const measureRef = once(_measureRef);\n\n const scalableHeight =\n fit === 'grow' && !fullScreenHandle.active ? maxHeight : '100%';\n\n const containerRef = useMemo(\n () =>\n debounce((root: HTMLDivElement) => {\n if (!root) {\n return;\n }\n measureRef(root);\n\n // Set up zooming + panning\n const node: SVGSVGElement = root.querySelector(\n `svg#${DEPENDENCY_GRAPH_SVG}`,\n ) as SVGSVGElement;\n if (!node) {\n return;\n }\n const container = d3Selection.select<SVGSVGElement, null>(node);\n const workspace = d3Selection.select(node.getElementById(WORKSPACE_ID));\n\n function enableZoom() {\n container.call(\n d3Zoom\n .zoom<SVGSVGElement, null>()\n .scaleExtent([1, Infinity])\n .on('zoom', event => {\n event.transform.x = Math.min(\n 0,\n Math.max(\n event.transform.x,\n maxWidth - maxWidth * event.transform.k,\n ),\n );\n event.transform.y = Math.min(\n 0,\n Math.max(\n event.transform.y,\n maxHeight - maxHeight * event.transform.k,\n ),\n );\n workspace.attr('transform', event.transform);\n }),\n );\n }\n\n if (zoom === 'enabled') {\n enableZoom();\n } else if (zoom === 'enable-on-click') {\n container.on('click', () => enableZoom());\n }\n\n const { width: newContainerWidth, height: newContainerHeight } =\n root.getBoundingClientRect();\n if (\n containerWidth !== newContainerWidth &&\n newContainerWidth <= maxWidth\n ) {\n setContainerWidth(newContainerWidth);\n }\n if (\n containerHeight !== newContainerHeight &&\n newContainerHeight <= maxHeight\n ) {\n setContainerHeight(newContainerHeight);\n }\n }, 100),\n [measureRef, containerHeight, containerWidth, maxWidth, maxHeight, zoom],\n );\n\n const setNodesAndEdges = useCallback(() => {\n // Cleaning up lingering nodes and edges\n const currentGraphNodes = graph.current.nodes();\n const currentGraphEdges = graph.current.edges();\n\n currentGraphNodes.forEach(nodeId => {\n const remainingNode = nodes.some(node => node.id === nodeId);\n if (!remainingNode) {\n graph.current.removeNode(nodeId);\n }\n });\n\n currentGraphEdges.forEach(e => {\n const remainingEdge = edges.some(\n edge => edge.from === e.v && edge.to === e.w,\n );\n if (!remainingEdge) {\n graph.current.removeEdge(e.v, e.w);\n }\n });\n\n // Adding/updating nodes and edges\n nodes.forEach(node => {\n const existingNode = graph.current\n .nodes()\n .find(nodeId => node.id === nodeId);\n\n if (existingNode && graph.current.node(existingNode)) {\n const { width, height, x, y } = graph.current.node(existingNode);\n graph.current.setNode(existingNode, { ...node, width, height, x, y });\n } else {\n graph.current.setNode(node.id, { ...node, width: 0, height: 0 });\n }\n });\n\n edges.forEach(e => {\n graph.current.setEdge(e.from, e.to, {\n ...e,\n label: e.label,\n width: 0,\n height: 0,\n labelpos: labelPosition,\n labeloffset: labelOffset,\n weight: edgeWeight,\n minlen: edgeRanks,\n });\n });\n }, [edges, nodes, labelPosition, labelOffset, edgeWeight, edgeRanks]);\n\n const updateGraph = useMemo(\n () =>\n debounce(\n () => {\n dagre.layout(graph.current);\n const { height, width } = graph.current.graph();\n const newHeight = Math.max(0, height || 0);\n const newWidth = Math.max(0, width || 0);\n setGraphWidth(newWidth);\n setGraphHeight(newHeight);\n\n setGraphNodes(graph.current.nodes());\n setGraphEdges(graph.current.edges());\n },\n 250,\n { leading: true },\n ),\n [],\n );\n\n useEffect(() => {\n graph.current.setGraph({\n rankdir: direction,\n align,\n nodesep: nodeMargin,\n edgesep: edgeMargin,\n ranksep: rankMargin,\n marginx: paddingX,\n marginy: paddingY,\n acyclicer,\n ranker,\n });\n\n setNodesAndEdges();\n updateGraph();\n\n return updateGraph.cancel;\n }, [\n acyclicer,\n align,\n direction,\n edgeMargin,\n paddingX,\n paddingY,\n nodeMargin,\n rankMargin,\n ranker,\n setNodesAndEdges,\n updateGraph,\n ]);\n\n const setNode = useCallback(\n (id: string, node: Types.DependencyNode<NodeData>) => {\n graph.current.setNode(id, node);\n updateGraph();\n return graph.current;\n },\n [updateGraph],\n );\n\n const setEdge = useCallback(\n (id: dagre.Edge, edge: Types.DependencyEdge<EdgeData>) => {\n graph.current.setEdge(id, edge);\n updateGraph();\n return graph.current;\n },\n [updateGraph],\n );\n\n return (\n <FullScreen\n handle={fullScreenHandle}\n className={classNames(\n fullScreenHandle.active ? styles.fullscreen : styles.root,\n )}\n >\n {allowFullscreen && (\n <Tooltip title={t('dependencyGraph.fullscreenTooltip')}>\n <IconButton\n className={styles.fullscreenButton}\n onClick={\n fullScreenHandle.active\n ? fullScreenHandle.exit\n : fullScreenHandle.enter\n }\n >\n {fullScreenHandle.active ? (\n <FullscreenExitIcon />\n ) : (\n <FullscreenIcon />\n )}\n </IconButton>\n </Tooltip>\n )}\n\n <div ref={containerRef} style={{ width: '100%', height: '100%' }}>\n <svg\n {...svgProps}\n width=\"100%\"\n height={scalableHeight}\n viewBox={`0 0 ${maxWidth} ${maxHeight}`}\n id={DEPENDENCY_GRAPH_SVG}\n >\n <defs>\n <marker\n id={ARROW_MARKER_ID}\n viewBox=\"0 0 24 24\"\n markerWidth=\"14\"\n markerHeight=\"14\"\n refX=\"16\"\n refY=\"12\"\n orient=\"auto\"\n markerUnits=\"strokeWidth\"\n >\n <path\n fill={theme.palette.textSubtle}\n d=\"M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z\"\n />\n </marker>\n {defs}\n </defs>\n <g id={WORKSPACE_ID}>\n <svg\n width={graphWidth}\n height={graphHeight}\n y={maxHeight / 2 - graphHeight / 2}\n x={maxWidth / 2 - graphWidth / 2}\n viewBox={`0 0 ${graphWidth} ${graphHeight}`}\n >\n {graphEdges.map(e => {\n const edge = graph.current.edge(e) as GraphEdge<EdgeData>;\n if (!edge) return null;\n if (renderEdge) return renderEdge({ edge, id: e });\n\n return (\n <Edge\n key={`${e.v}-${e.w}`}\n id={e}\n setEdge={setEdge}\n render={renderLabel}\n edge={edge}\n curve={curve}\n showArrowHeads={showArrowHeads}\n />\n );\n })}\n {graphNodes.map((id: string) => {\n const node = graph.current.node(id);\n if (!node) return null;\n return (\n <Node\n key={id}\n setNode={setNode}\n render={renderNode}\n node={node}\n />\n );\n })}\n </svg>\n </g>\n </svg>\n </div>\n </FullScreen>\n );\n}\n"],"names":["Types"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA6CA,MAAM,SAAA,GAAY,UAAA,CAAW,CAAC,KAAA,MAAkB;AAAA,EAC9C,gBAAA,EAAkB;AAAA,IAChB,QAAA,EAAU,UAAA;AAAA,IACV,KAAA,EAAO;AAAA,GACT;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,QAAA,EAAU,QAAA;AAAA,IACV,SAAA,EAAW,MAAA;AAAA,IACX,QAAA,EAAU;AAAA,GACZ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,SAAA,EAAW;AAAA,GACb;AAAA,EACA,UAAA,EAAY;AAAA,IACV,eAAA,EAAiB,KAAA,CAAM,OAAA,CAAQ,UAAA,CAAW;AAAA;AAE9C,CAAA,CAAE,CAAA;AAqKF,MAAM,YAAA,GAAe,WAAA;AACrB,MAAM,oBAAA,GAAuB,kBAAA;AAOtB,SAAS,gBACd,KAAA,EACA;AACA,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,KAAA;AAAA,IACA,UAAA;AAAA,IACA,SAAA,GAAYA,qBAAM,SAAA,CAAU,UAAA;AAAA,IAC5B,KAAA;AAAA,IACA,UAAA,GAAa,EAAA;AAAA,IACb,UAAA,GAAa,EAAA;AAAA,IACb,UAAA,GAAa,EAAA;AAAA,IACb,QAAA,GAAW,CAAA;AAAA,IACX,QAAA,GAAW,CAAA;AAAA,IACX,SAAA;AAAA,IACA,MAAA,GAASA,qBAAM,MAAA,CAAO,eAAA;AAAA,IACtB,aAAA,GAAgBA,qBAAM,aAAA,CAAc,KAAA;AAAA,IACpC,WAAA,GAAc,EAAA;AAAA,IACd,SAAA,GAAY,CAAA;AAAA,IACZ,UAAA,GAAa,CAAA;AAAA,IACb,UAAA;AAAA,IACA,WAAA;AAAA,IACA,IAAA;AAAA,IACA,IAAA,GAAO,SAAA;AAAA,IACP,KAAA,GAAQ,gBAAA;AAAA,IACR,cAAA,GAAiB,KAAA;AAAA,IACjB,GAAA,GAAM,MAAA;AAAA,IACN,eAAA,GAAkB,IAAA;AAAA,IAClB,GAAG;AAAA,GACL,GAAI,KAAA;AACJ,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAAiB,GAAG,CAAA;AAChE,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAAiB,GAAG,CAAA;AAClE,EAAA,MAAM,mBAAmB,mBAAA,EAAoB;AAC7C,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,MAAM,EAAE,CAAA,EAAE,GAAI,iBAAA,CAAkB,4BAA4B,CAAA;AAE5D,EAAA,MAAM,KAAA,GAAQ,MAAA;AAAA,IACZ,IAAI,KAAA,CAAM,QAAA,CAAS,KAAA;AAAM,GAC3B;AACA,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,QAAA;AAAA,IAClC,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAM,EAAG,KAAA,IAAS;AAAA,GAClC;AACA,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,QAAA;AAAA,IACpC,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAM,EAAG,MAAA,IAAU;AAAA,GACnC;AACA,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,QAAA,CAAmB,EAAE,CAAA;AACzD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,QAAA,CAAuB,EAAE,CAAA;AAE7D,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,UAAA,EAAY,cAAc,CAAA;AACpD,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,eAAe,CAAA;AAEvD,EAAA,MAAM,CAAC,WAAW,CAAA,GAAI,UAAA,EAAW;AACjC,EAAA,MAAM,UAAA,GAAa,KAAK,WAAW,CAAA;AAEnC,EAAA,MAAM,iBACJ,GAAA,KAAQ,MAAA,IAAU,CAAC,gBAAA,CAAiB,SAAS,SAAA,GAAY,MAAA;AAE3D,EAAA,MAAM,YAAA,GAAe,OAAA;AAAA,IACnB,MACE,QAAA,CAAS,CAAC,IAAA,KAAyB;AACjC,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA;AAAA,MACF;AACA,MAAA,UAAA,CAAW,IAAI,CAAA;AAGf,MAAA,MAAM,OAAsB,IAAA,CAAK,aAAA;AAAA,QAC/B,OAAO,oBAAoB,CAAA;AAAA,OAC7B;AACA,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA;AAAA,MACF;AACA,MAAA,MAAM,SAAA,GAAY,WAAA,CAAY,MAAA,CAA4B,IAAI,CAAA;AAC9D,MAAA,MAAM,YAAY,WAAA,CAAY,MAAA,CAAO,IAAA,CAAK,cAAA,CAAe,YAAY,CAAC,CAAA;AAEtE,MAAA,SAAS,UAAA,GAAa;AACpB,QAAA,SAAA,CAAU,IAAA;AAAA,UACR,MAAA,CACG,IAAA,EAA0B,CAC1B,WAAA,CAAY,CAAC,CAAA,EAAG,QAAQ,CAAC,CAAA,CACzB,EAAA,CAAG,MAAA,EAAQ,CAAA,KAAA,KAAS;AACnB,YAAA,KAAA,CAAM,SAAA,CAAU,IAAI,IAAA,CAAK,GAAA;AAAA,cACvB,CAAA;AAAA,cACA,IAAA,CAAK,GAAA;AAAA,gBACH,MAAM,SAAA,CAAU,CAAA;AAAA,gBAChB,QAAA,GAAW,QAAA,GAAW,KAAA,CAAM,SAAA,CAAU;AAAA;AACxC,aACF;AACA,YAAA,KAAA,CAAM,SAAA,CAAU,IAAI,IAAA,CAAK,GAAA;AAAA,cACvB,CAAA;AAAA,cACA,IAAA,CAAK,GAAA;AAAA,gBACH,MAAM,SAAA,CAAU,CAAA;AAAA,gBAChB,SAAA,GAAY,SAAA,GAAY,KAAA,CAAM,SAAA,CAAU;AAAA;AAC1C,aACF;AACA,YAAA,SAAA,CAAU,IAAA,CAAK,WAAA,EAAa,KAAA,CAAM,SAAS,CAAA;AAAA,UAC7C,CAAC;AAAA,SACL;AAAA,MACF;AAEA,MAAA,IAAI,SAAS,SAAA,EAAW;AACtB,QAAA,UAAA,EAAW;AAAA,MACb,CAAA,MAAA,IAAW,SAAS,iBAAA,EAAmB;AACrC,QAAA,SAAA,CAAU,EAAA,CAAG,OAAA,EAAS,MAAM,UAAA,EAAY,CAAA;AAAA,MAC1C;AAEA,MAAA,MAAM,EAAE,KAAA,EAAO,iBAAA,EAAmB,QAAQ,kBAAA,EAAmB,GAC3D,KAAK,qBAAA,EAAsB;AAC7B,MAAA,IACE,cAAA,KAAmB,iBAAA,IACnB,iBAAA,IAAqB,QAAA,EACrB;AACA,QAAA,iBAAA,CAAkB,iBAAiB,CAAA;AAAA,MACrC;AACA,MAAA,IACE,eAAA,KAAoB,kBAAA,IACpB,kBAAA,IAAsB,SAAA,EACtB;AACA,QAAA,kBAAA,CAAmB,kBAAkB,CAAA;AAAA,MACvC;AAAA,IACF,GAAG,GAAG,CAAA;AAAA,IACR,CAAC,UAAA,EAAY,eAAA,EAAiB,cAAA,EAAgB,QAAA,EAAU,WAAW,IAAI;AAAA,GACzE;AAEA,EAAA,MAAM,gBAAA,GAAmB,YAAY,MAAM;AAEzC,IAAA,MAAM,iBAAA,GAAoB,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAM;AAC9C,IAAA,MAAM,iBAAA,GAAoB,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAM;AAE9C,IAAA,iBAAA,CAAkB,QAAQ,CAAA,MAAA,KAAU;AAClC,MAAA,MAAM,gBAAgB,KAAA,CAAM,IAAA,CAAK,CAAA,IAAA,KAAQ,IAAA,CAAK,OAAO,MAAM,CAAA;AAC3D,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA,KAAA,CAAM,OAAA,CAAQ,WAAW,MAAM,CAAA;AAAA,MACjC;AAAA,IACF,CAAC,CAAA;AAED,IAAA,iBAAA,CAAkB,QAAQ,CAAA,CAAA,KAAK;AAC7B,MAAA,MAAM,gBAAgB,KAAA,CAAM,IAAA;AAAA,QAC1B,UAAQ,IAAA,CAAK,IAAA,KAAS,EAAE,CAAA,IAAK,IAAA,CAAK,OAAO,CAAA,CAAE;AAAA,OAC7C;AACA,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA,KAAA,CAAM,OAAA,CAAQ,UAAA,CAAW,CAAA,CAAE,CAAA,EAAG,EAAE,CAAC,CAAA;AAAA,MACnC;AAAA,IACF,CAAC,CAAA;AAGD,IAAA,KAAA,CAAM,QAAQ,CAAA,IAAA,KAAQ;AACpB,MAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CACxB,KAAA,GACA,IAAA,CAAK,CAAA,MAAA,KAAU,IAAA,CAAK,EAAA,KAAO,MAAM,CAAA;AAEpC,MAAA,IAAI,YAAA,IAAgB,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,YAAY,CAAA,EAAG;AACpD,QAAA,MAAM,EAAE,OAAO,MAAA,EAAQ,CAAA,EAAG,GAAE,GAAI,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,YAAY,CAAA;AAC/D,QAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,YAAA,EAAc,EAAE,GAAG,MAAM,KAAA,EAAO,MAAA,EAAQ,CAAA,EAAG,CAAA,EAAG,CAAA;AAAA,MACtE,CAAA,MAAO;AACL,QAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,EAAA,EAAI,EAAE,GAAG,IAAA,EAAM,KAAA,EAAO,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,CAAA;AAAA,MACjE;AAAA,IACF,CAAC,CAAA;AAED,IAAA,KAAA,CAAM,QAAQ,CAAA,CAAA,KAAK;AACjB,MAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,CAAA,CAAE,IAAA,EAAM,EAAE,EAAA,EAAI;AAAA,QAClC,GAAG,CAAA;AAAA,QACH,OAAO,CAAA,CAAE,KAAA;AAAA,QACT,KAAA,EAAO,CAAA;AAAA,QACP,MAAA,EAAQ,CAAA;AAAA,QACR,QAAA,EAAU,aAAA;AAAA,QACV,WAAA,EAAa,WAAA;AAAA,QACb,MAAA,EAAQ,UAAA;AAAA,QACR,MAAA,EAAQ;AAAA,OACT,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,KAAA,EAAO,KAAA,EAAO,eAAe,WAAA,EAAa,UAAA,EAAY,SAAS,CAAC,CAAA;AAEpE,EAAA,MAAM,WAAA,GAAc,OAAA;AAAA,IAClB,MACE,QAAA;AAAA,MACE,MAAM;AACJ,QAAA,KAAA,CAAM,MAAA,CAAO,MAAM,OAAO,CAAA;AAC1B,QAAA,MAAM,EAAE,MAAA,EAAQ,KAAA,EAAM,GAAI,KAAA,CAAM,QAAQ,KAAA,EAAM;AAC9C,QAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,UAAU,CAAC,CAAA;AACzC,QAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAS,CAAC,CAAA;AACvC,QAAA,aAAA,CAAc,QAAQ,CAAA;AACtB,QAAA,cAAA,CAAe,SAAS,CAAA;AAExB,QAAA,aAAA,CAAc,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,CAAA;AACnC,QAAA,aAAA,CAAc,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,CAAA;AAAA,MACrC,CAAA;AAAA,MACA,GAAA;AAAA,MACA,EAAE,SAAS,IAAA;AAAK,KAClB;AAAA,IACF;AAAC,GACH;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,KAAA,CAAM,QAAQ,QAAA,CAAS;AAAA,MACrB,OAAA,EAAS,SAAA;AAAA,MACT,KAAA;AAAA,MACA,OAAA,EAAS,UAAA;AAAA,MACT,OAAA,EAAS,UAAA;AAAA,MACT,OAAA,EAAS,UAAA;AAAA,MACT,OAAA,EAAS,QAAA;AAAA,MACT,OAAA,EAAS,QAAA;AAAA,MACT,SAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,gBAAA,EAAiB;AACjB,IAAA,WAAA,EAAY;AAEZ,IAAA,OAAO,WAAA,CAAY,MAAA;AAAA,EACrB,CAAA,EAAG;AAAA,IACD,SAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,MAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,IAAY,IAAA,KAAyC;AACpD,MAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,EAAA,EAAI,IAAI,CAAA;AAC9B,MAAA,WAAA,EAAY;AACZ,MAAA,OAAO,KAAA,CAAM,OAAA;AAAA,IACf,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,IAAgB,IAAA,KAAyC;AACxD,MAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,EAAA,EAAI,IAAI,CAAA;AAC9B,MAAA,WAAA,EAAY;AACZ,MAAA,OAAO,KAAA,CAAM,OAAA;AAAA,IACf,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,uBACE,IAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,MAAA,EAAQ,gBAAA;AAAA,MACR,SAAA,EAAW,UAAA;AAAA,QACT,gBAAA,CAAiB,MAAA,GAAS,MAAA,CAAO,UAAA,GAAa,MAAA,CAAO;AAAA,OACvD;AAAA,MAEC,QAAA,EAAA;AAAA,QAAA,eAAA,oBACC,GAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAO,CAAA,CAAE,mCAAmC,CAAA,EACnD,QAAA,kBAAA,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,WAAW,MAAA,CAAO,gBAAA;AAAA,YAClB,OAAA,EACE,gBAAA,CAAiB,MAAA,GACb,gBAAA,CAAiB,OACjB,gBAAA,CAAiB,KAAA;AAAA,YAGtB,2BAAiB,MAAA,mBAChB,GAAA,CAAC,kBAAA,EAAA,EAAmB,CAAA,uBAEnB,cAAA,EAAA,EAAe;AAAA;AAAA,SAEpB,EACF,CAAA;AAAA,wBAGF,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,YAAA,EAAc,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAO,EAC7D,QAAA,kBAAA,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACE,GAAG,QAAA;AAAA,YACJ,KAAA,EAAM,MAAA;AAAA,YACN,MAAA,EAAQ,cAAA;AAAA,YACR,OAAA,EAAS,CAAA,IAAA,EAAO,QAAQ,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAAA,YACrC,EAAA,EAAI,oBAAA;AAAA,YAEJ,QAAA,EAAA;AAAA,8BAAA,IAAA,CAAC,MAAA,EAAA,EACC,QAAA,EAAA;AAAA,gCAAA,GAAA;AAAA,kBAAC,QAAA;AAAA,kBAAA;AAAA,oBACC,EAAA,EAAI,eAAA;AAAA,oBACJ,OAAA,EAAQ,WAAA;AAAA,oBACR,WAAA,EAAY,IAAA;AAAA,oBACZ,YAAA,EAAa,IAAA;AAAA,oBACb,IAAA,EAAK,IAAA;AAAA,oBACL,IAAA,EAAK,IAAA;AAAA,oBACL,MAAA,EAAO,MAAA;AAAA,oBACP,WAAA,EAAY,aAAA;AAAA,oBAEZ,QAAA,kBAAA,GAAA;AAAA,sBAAC,MAAA;AAAA,sBAAA;AAAA,wBACC,IAAA,EAAM,MAAM,OAAA,CAAQ,UAAA;AAAA,wBACpB,CAAA,EAAE;AAAA;AAAA;AACJ;AAAA,iBACF;AAAA,gBACC;AAAA,eAAA,EACH,CAAA;AAAA,8BACA,GAAA,CAAC,GAAA,EAAA,EAAE,EAAA,EAAI,YAAA,EACL,QAAA,kBAAA,IAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,KAAA,EAAO,UAAA;AAAA,kBACP,MAAA,EAAQ,WAAA;AAAA,kBACR,CAAA,EAAG,SAAA,GAAY,CAAA,GAAI,WAAA,GAAc,CAAA;AAAA,kBACjC,CAAA,EAAG,QAAA,GAAW,CAAA,GAAI,UAAA,GAAa,CAAA;AAAA,kBAC/B,OAAA,EAAS,CAAA,IAAA,EAAO,UAAU,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA;AAAA,kBAExC,QAAA,EAAA;AAAA,oBAAA,UAAA,CAAW,IAAI,CAAA,CAAA,KAAK;AACnB,sBAAA,MAAM,IAAA,GAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA;AACjC,sBAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,sBAAA,IAAI,YAAY,OAAO,UAAA,CAAW,EAAE,IAAA,EAAM,EAAA,EAAI,GAAG,CAAA;AAEjD,sBAAA,uBACE,GAAA;AAAA,wBAAC,IAAA;AAAA,wBAAA;AAAA,0BAEC,EAAA,EAAI,CAAA;AAAA,0BACJ,OAAA;AAAA,0BACA,MAAA,EAAQ,WAAA;AAAA,0BACR,IAAA;AAAA,0BACA,KAAA;AAAA,0BACA;AAAA,yBAAA;AAAA,wBANK,CAAA,EAAG,CAAA,CAAE,CAAC,CAAA,CAAA,EAAI,EAAE,CAAC,CAAA;AAAA,uBAOpB;AAAA,oBAEJ,CAAC,CAAA;AAAA,oBACA,UAAA,CAAW,GAAA,CAAI,CAAC,EAAA,KAAe;AAC9B,sBAAA,MAAM,IAAA,GAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAA;AAClC,sBAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,sBAAA,uBACE,GAAA;AAAA,wBAAC,IAAA;AAAA,wBAAA;AAAA,0BAEC,OAAA;AAAA,0BACA,MAAA,EAAQ,UAAA;AAAA,0BACR;AAAA,yBAAA;AAAA,wBAHK;AAAA,uBAIP;AAAA,oBAEJ,CAAC;AAAA;AAAA;AAAA,eACH,EACF;AAAA;AAAA;AAAA,SACF,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
1
+ {"version":3,"file":"DependencyGraph.esm.js","sources":["../../../src/components/DependencyGraph/DependencyGraph.tsx"],"sourcesContent":["/*\n * Copyright 2020 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 { type ComponentType, Suspense, lazy } from 'react';\nimport type { DependencyGraphProps } from './DependencyGraphContent';\n\nexport type { DependencyGraphProps } from './DependencyGraphContent';\n\nconst LazyDependencyGraphContent = lazy(() =>\n import('./DependencyGraphContent').then(m => ({\n default: m.DependencyGraph as ComponentType<any>,\n })),\n);\n\n/**\n * Graph component used to visualize relations between entities\n *\n * @public\n */\nexport function DependencyGraph<NodeData, EdgeData>(\n props: DependencyGraphProps<NodeData, EdgeData>,\n) {\n return (\n <Suspense fallback={<div />}>\n <LazyDependencyGraphContent {...props} />\n </Suspense>\n );\n}\n"],"names":[],"mappings":";;;AAqBA,MAAM,0BAAA,GAA6B,IAAA;AAAA,EAAK,MACtC,OAAO,iCAA0B,CAAA,CAAE,KAAK,CAAA,CAAA,MAAM;AAAA,IAC5C,SAAS,CAAA,CAAE;AAAA,GACb,CAAE;AACJ,CAAA;AAOO,SAAS,gBACd,KAAA,EACA;AACA,EAAA,uBACE,GAAA,CAAC,QAAA,EAAA,EAAS,QAAA,kBAAU,GAAA,CAAC,KAAA,EAAA,EAAI,GACvB,QAAA,kBAAA,GAAA,CAAC,0BAAA,EAAA,EAA4B,GAAG,KAAA,EAAO,CAAA,EACzC,CAAA;AAEJ;;;;"}
@@ -0,0 +1,342 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useState, useRef, useMemo, useCallback, useEffect } from 'react';
3
+ import useMeasure from 'react-use/esm/useMeasure';
4
+ import classNames from 'classnames';
5
+ import { once } from 'lodash';
6
+ import * as d3Zoom from 'd3-zoom';
7
+ import * as d3Selection from 'd3-selection';
8
+ import useTheme from '@material-ui/core/styles/useTheme';
9
+ import dagre from '@dagrejs/dagre';
10
+ import debounce from 'lodash/debounce';
11
+ import { DependencyGraphTypes } from './types.esm.js';
12
+ import { Node } from './Node.esm.js';
13
+ import { Edge } from './Edge.esm.js';
14
+ import { ARROW_MARKER_ID } from './constants.esm.js';
15
+ import IconButton from '@material-ui/core/IconButton';
16
+ import FullscreenIcon from '@material-ui/icons/Fullscreen';
17
+ import FullscreenExitIcon from '@material-ui/icons/FullscreenExit';
18
+ import { useFullScreenHandle, FullScreen } from 'react-full-screen';
19
+ import { makeStyles } from '@material-ui/core/styles';
20
+ import Tooltip from '@material-ui/core/Tooltip';
21
+ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
22
+ import { coreComponentsTranslationRef } from '../../translation.esm.js';
23
+
24
+ const useStyles = makeStyles((theme) => ({
25
+ fullscreenButton: {
26
+ position: "absolute",
27
+ right: 0
28
+ },
29
+ root: {
30
+ overflow: "hidden",
31
+ minHeight: "100%",
32
+ minWidth: "100%"
33
+ },
34
+ fixedHeight: {
35
+ maxHeight: "100%"
36
+ },
37
+ fullscreen: {
38
+ backgroundColor: theme.palette.background.paper
39
+ }
40
+ }));
41
+ const WORKSPACE_ID = "workspace";
42
+ const DEPENDENCY_GRAPH_SVG = "dependency-graph";
43
+ function DependencyGraph(props) {
44
+ const {
45
+ edges,
46
+ nodes,
47
+ renderNode,
48
+ direction = DependencyGraphTypes.Direction.TOP_BOTTOM,
49
+ align,
50
+ nodeMargin = 50,
51
+ edgeMargin = 10,
52
+ rankMargin = 50,
53
+ paddingX = 0,
54
+ paddingY = 0,
55
+ acyclicer,
56
+ ranker = DependencyGraphTypes.Ranker.NETWORK_SIMPLEX,
57
+ labelPosition = DependencyGraphTypes.LabelPosition.RIGHT,
58
+ labelOffset = 10,
59
+ edgeRanks = 1,
60
+ edgeWeight = 1,
61
+ renderEdge,
62
+ renderLabel,
63
+ defs,
64
+ zoom = "enabled",
65
+ curve = "curveMonotoneX",
66
+ showArrowHeads = false,
67
+ fit = "grow",
68
+ allowFullscreen = true,
69
+ ...svgProps
70
+ } = props;
71
+ const theme = useTheme();
72
+ const [containerWidth, setContainerWidth] = useState(100);
73
+ const [containerHeight, setContainerHeight] = useState(100);
74
+ const fullScreenHandle = useFullScreenHandle();
75
+ const styles = useStyles();
76
+ const { t } = useTranslationRef(coreComponentsTranslationRef);
77
+ const graph = useRef(
78
+ new dagre.graphlib.Graph()
79
+ );
80
+ const [graphWidth, setGraphWidth] = useState(
81
+ graph.current.graph()?.width || 0
82
+ );
83
+ const [graphHeight, setGraphHeight] = useState(
84
+ graph.current.graph()?.height || 0
85
+ );
86
+ const [graphNodes, setGraphNodes] = useState([]);
87
+ const [graphEdges, setGraphEdges] = useState([]);
88
+ const maxWidth = Math.max(graphWidth, containerWidth);
89
+ const maxHeight = Math.max(graphHeight, containerHeight);
90
+ const [_measureRef] = useMeasure();
91
+ const measureRef = once(_measureRef);
92
+ const scalableHeight = fit === "grow" && !fullScreenHandle.active ? maxHeight : "100%";
93
+ const containerRef = useMemo(
94
+ () => debounce((root) => {
95
+ if (!root) {
96
+ return;
97
+ }
98
+ measureRef(root);
99
+ const node = root.querySelector(
100
+ `svg#${DEPENDENCY_GRAPH_SVG}`
101
+ );
102
+ if (!node) {
103
+ return;
104
+ }
105
+ const container = d3Selection.select(node);
106
+ const workspace = d3Selection.select(node.getElementById(WORKSPACE_ID));
107
+ function enableZoom() {
108
+ container.call(
109
+ d3Zoom.zoom().scaleExtent([1, Infinity]).on("zoom", (event) => {
110
+ event.transform.x = Math.min(
111
+ 0,
112
+ Math.max(
113
+ event.transform.x,
114
+ maxWidth - maxWidth * event.transform.k
115
+ )
116
+ );
117
+ event.transform.y = Math.min(
118
+ 0,
119
+ Math.max(
120
+ event.transform.y,
121
+ maxHeight - maxHeight * event.transform.k
122
+ )
123
+ );
124
+ workspace.attr("transform", event.transform);
125
+ })
126
+ );
127
+ }
128
+ if (zoom === "enabled") {
129
+ enableZoom();
130
+ } else if (zoom === "enable-on-click") {
131
+ container.on("click", () => enableZoom());
132
+ }
133
+ const { width: newContainerWidth, height: newContainerHeight } = root.getBoundingClientRect();
134
+ if (containerWidth !== newContainerWidth && newContainerWidth <= maxWidth) {
135
+ setContainerWidth(newContainerWidth);
136
+ }
137
+ if (containerHeight !== newContainerHeight && newContainerHeight <= maxHeight) {
138
+ setContainerHeight(newContainerHeight);
139
+ }
140
+ }, 100),
141
+ [measureRef, containerHeight, containerWidth, maxWidth, maxHeight, zoom]
142
+ );
143
+ const setNodesAndEdges = useCallback(() => {
144
+ const currentGraphNodes = graph.current.nodes();
145
+ const currentGraphEdges = graph.current.edges();
146
+ currentGraphNodes.forEach((nodeId) => {
147
+ const remainingNode = nodes.some((node) => node.id === nodeId);
148
+ if (!remainingNode) {
149
+ graph.current.removeNode(nodeId);
150
+ }
151
+ });
152
+ currentGraphEdges.forEach((e) => {
153
+ const remainingEdge = edges.some(
154
+ (edge) => edge.from === e.v && edge.to === e.w
155
+ );
156
+ if (!remainingEdge) {
157
+ graph.current.removeEdge(e.v, e.w);
158
+ }
159
+ });
160
+ nodes.forEach((node) => {
161
+ const existingNode = graph.current.nodes().find((nodeId) => node.id === nodeId);
162
+ if (existingNode && graph.current.node(existingNode)) {
163
+ const { width, height, x, y } = graph.current.node(existingNode);
164
+ graph.current.setNode(existingNode, { ...node, width, height, x, y });
165
+ } else {
166
+ graph.current.setNode(node.id, { ...node, width: 0, height: 0 });
167
+ }
168
+ });
169
+ edges.forEach((e) => {
170
+ graph.current.setEdge(e.from, e.to, {
171
+ ...e,
172
+ label: e.label,
173
+ width: 0,
174
+ height: 0,
175
+ labelpos: labelPosition,
176
+ labeloffset: labelOffset,
177
+ weight: edgeWeight,
178
+ minlen: edgeRanks
179
+ });
180
+ });
181
+ }, [edges, nodes, labelPosition, labelOffset, edgeWeight, edgeRanks]);
182
+ const updateGraph = useMemo(
183
+ () => debounce(
184
+ () => {
185
+ dagre.layout(graph.current);
186
+ const { height, width } = graph.current.graph();
187
+ const newHeight = Math.max(0, height || 0);
188
+ const newWidth = Math.max(0, width || 0);
189
+ setGraphWidth(newWidth);
190
+ setGraphHeight(newHeight);
191
+ setGraphNodes(graph.current.nodes());
192
+ setGraphEdges(graph.current.edges());
193
+ },
194
+ 250,
195
+ { leading: true }
196
+ ),
197
+ []
198
+ );
199
+ useEffect(() => {
200
+ graph.current.setGraph({
201
+ rankdir: direction,
202
+ align,
203
+ nodesep: nodeMargin,
204
+ edgesep: edgeMargin,
205
+ ranksep: rankMargin,
206
+ marginx: paddingX,
207
+ marginy: paddingY,
208
+ acyclicer,
209
+ ranker
210
+ });
211
+ setNodesAndEdges();
212
+ updateGraph();
213
+ return updateGraph.cancel;
214
+ }, [
215
+ acyclicer,
216
+ align,
217
+ direction,
218
+ edgeMargin,
219
+ paddingX,
220
+ paddingY,
221
+ nodeMargin,
222
+ rankMargin,
223
+ ranker,
224
+ setNodesAndEdges,
225
+ updateGraph
226
+ ]);
227
+ const setNode = useCallback(
228
+ (id, node) => {
229
+ graph.current.setNode(id, node);
230
+ updateGraph();
231
+ return graph.current;
232
+ },
233
+ [updateGraph]
234
+ );
235
+ const setEdge = useCallback(
236
+ (id, edge) => {
237
+ graph.current.setEdge(id, edge);
238
+ updateGraph();
239
+ return graph.current;
240
+ },
241
+ [updateGraph]
242
+ );
243
+ return /* @__PURE__ */ jsxs(
244
+ FullScreen,
245
+ {
246
+ handle: fullScreenHandle,
247
+ className: classNames(
248
+ fullScreenHandle.active ? styles.fullscreen : styles.root
249
+ ),
250
+ children: [
251
+ allowFullscreen && /* @__PURE__ */ jsx(Tooltip, { title: t("dependencyGraph.fullscreenTooltip"), children: /* @__PURE__ */ jsx(
252
+ IconButton,
253
+ {
254
+ className: styles.fullscreenButton,
255
+ onClick: fullScreenHandle.active ? fullScreenHandle.exit : fullScreenHandle.enter,
256
+ children: fullScreenHandle.active ? /* @__PURE__ */ jsx(FullscreenExitIcon, {}) : /* @__PURE__ */ jsx(FullscreenIcon, {})
257
+ }
258
+ ) }),
259
+ /* @__PURE__ */ jsx("div", { ref: containerRef, style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxs(
260
+ "svg",
261
+ {
262
+ ...svgProps,
263
+ width: "100%",
264
+ height: scalableHeight,
265
+ viewBox: `0 0 ${maxWidth} ${maxHeight}`,
266
+ id: DEPENDENCY_GRAPH_SVG,
267
+ children: [
268
+ /* @__PURE__ */ jsxs("defs", { children: [
269
+ /* @__PURE__ */ jsx(
270
+ "marker",
271
+ {
272
+ id: ARROW_MARKER_ID,
273
+ viewBox: "0 0 24 24",
274
+ markerWidth: "14",
275
+ markerHeight: "14",
276
+ refX: "16",
277
+ refY: "12",
278
+ orient: "auto",
279
+ markerUnits: "strokeWidth",
280
+ children: /* @__PURE__ */ jsx(
281
+ "path",
282
+ {
283
+ fill: theme.palette.textSubtle,
284
+ d: "M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"
285
+ }
286
+ )
287
+ }
288
+ ),
289
+ defs
290
+ ] }),
291
+ /* @__PURE__ */ jsx("g", { id: WORKSPACE_ID, children: /* @__PURE__ */ jsxs(
292
+ "svg",
293
+ {
294
+ width: graphWidth,
295
+ height: graphHeight,
296
+ y: maxHeight / 2 - graphHeight / 2,
297
+ x: maxWidth / 2 - graphWidth / 2,
298
+ viewBox: `0 0 ${graphWidth} ${graphHeight}`,
299
+ children: [
300
+ graphEdges.map((e) => {
301
+ const edge = graph.current.edge(e);
302
+ if (!edge) return null;
303
+ if (renderEdge) return renderEdge({ edge, id: e });
304
+ return /* @__PURE__ */ jsx(
305
+ Edge,
306
+ {
307
+ id: e,
308
+ setEdge,
309
+ render: renderLabel,
310
+ edge,
311
+ curve,
312
+ showArrowHeads
313
+ },
314
+ `${e.v}-${e.w}`
315
+ );
316
+ }),
317
+ graphNodes.map((id) => {
318
+ const node = graph.current.node(id);
319
+ if (!node) return null;
320
+ return /* @__PURE__ */ jsx(
321
+ Node,
322
+ {
323
+ setNode,
324
+ render: renderNode,
325
+ node
326
+ },
327
+ id
328
+ );
329
+ })
330
+ ]
331
+ }
332
+ ) })
333
+ ]
334
+ }
335
+ ) })
336
+ ]
337
+ }
338
+ );
339
+ }
340
+
341
+ export { DependencyGraph };
342
+ //# sourceMappingURL=DependencyGraphContent.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DependencyGraphContent.esm.js","sources":["../../../src/components/DependencyGraph/DependencyGraphContent.tsx"],"sourcesContent":["/*\n * Copyright 2020 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 SVGProps,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport useMeasure from 'react-use/esm/useMeasure';\nimport classNames from 'classnames';\nimport { once } from 'lodash';\nimport * as d3Zoom from 'd3-zoom';\nimport * as d3Selection from 'd3-selection';\nimport useTheme from '@material-ui/core/styles/useTheme';\nimport dagre from '@dagrejs/dagre';\nimport debounce from 'lodash/debounce';\nimport { DependencyGraphTypes as Types } from './types';\nimport { Node } from './Node';\nimport { Edge, GraphEdge } from './Edge';\nimport { ARROW_MARKER_ID } from './constants';\nimport IconButton from '@material-ui/core/IconButton';\nimport FullscreenIcon from '@material-ui/icons/Fullscreen';\nimport FullscreenExitIcon from '@material-ui/icons/FullscreenExit';\nimport { FullScreen, useFullScreenHandle } from 'react-full-screen';\nimport { makeStyles, Theme } from '@material-ui/core/styles';\nimport Tooltip from '@material-ui/core/Tooltip';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { coreComponentsTranslationRef } from '../../translation';\n\nconst useStyles = makeStyles((theme: Theme) => ({\n fullscreenButton: {\n position: 'absolute',\n right: 0,\n },\n root: {\n overflow: 'hidden',\n minHeight: '100%',\n minWidth: '100%',\n },\n fixedHeight: {\n maxHeight: '100%',\n },\n fullscreen: {\n backgroundColor: theme.palette.background.paper,\n },\n}));\n\n/**\n * Properties of {@link DependencyGraph}\n *\n * @public\n * @remarks\n * `<NodeData>` and `<EdgeData>` are useful when rendering custom or edge labels\n */\nexport interface DependencyGraphProps<NodeData, EdgeData>\n extends SVGProps<SVGSVGElement> {\n /**\n * Edges of graph\n */\n edges: Types.DependencyEdge<EdgeData>[];\n /**\n * Nodes of Graph\n */\n nodes: Types.DependencyNode<NodeData>[];\n /**\n * Graph {@link DependencyGraphTypes.(Direction:namespace) | direction}\n *\n * @remarks\n *\n * Default: `DependencyGraphTypes.Direction.TOP_BOTTOM`\n */\n direction?: Types.Direction;\n /**\n * Node {@link DependencyGraphTypes.(Alignment:namespace) | alignment}\n */\n align?: Types.Alignment;\n /**\n * Margin between nodes on each rank\n *\n * @remarks\n *\n * Default: 50\n */\n nodeMargin?: number;\n /**\n * Margin between edges\n *\n * @remarks\n *\n * Default: 10\n */\n edgeMargin?: number;\n /**\n * Margin between each rank\n *\n * @remarks\n *\n * Default: 50\n */\n rankMargin?: number;\n /**\n * Margin on left and right of whole graph\n *\n * @remarks\n *\n * Default: 0\n */\n paddingX?: number;\n /**\n * Margin on top and bottom of whole graph\n *\n * @remarks\n *\n * Default: 0\n */\n paddingY?: number;\n /**\n * Heuristic used to find set of edges that will make graph acyclic\n */\n acyclicer?: 'greedy';\n /**\n * {@link DependencyGraphTypes.(Ranker:namespace) | Algorithm} used to rank nodes\n *\n * @remarks\n *\n * Default: `DependencyGraphTypes.Ranker.NETWORK_SIMPLEX`\n */\n ranker?: Types.Ranker;\n /**\n * {@link DependencyGraphTypes.(LabelPosition:namespace) | Position} of label in relation to edge\n *\n * @remarks\n *\n * Default: `DependencyGraphTypes.LabelPosition.RIGHT`\n */\n labelPosition?: Types.LabelPosition;\n /**\n * How much to move label away from edge\n *\n * @remarks\n *\n * Applies only when {@link DependencyGraphProps.labelPosition} is `DependencyGraphTypes.LabelPosition.LEFT` or\n * `DependencyGraphTypes.LabelPosition.RIGHT`\n */\n labelOffset?: number;\n /**\n * Minimum number of ranks to keep between connected nodes\n */\n edgeRanks?: number;\n /**\n * Weight applied to edges in graph\n */\n edgeWeight?: number;\n /**\n * Custom edge rendering component\n */\n renderEdge?: Types.RenderEdgeFunction<EdgeData>;\n /**\n * Custom node rendering component\n */\n renderNode?: Types.RenderNodeFunction<NodeData>;\n /**\n * Custom label rendering component\n */\n renderLabel?: Types.RenderLabelFunction<EdgeData>;\n /**\n * {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs | Defs} shared by rendered SVG to be used by\n * {@link DependencyGraphProps.renderNode} and/or {@link DependencyGraphProps.renderLabel}\n */\n defs?: JSX.Element | JSX.Element[];\n /**\n * Controls zoom behavior of graph\n *\n * @remarks\n *\n * Default: `enabled`\n */\n zoom?: 'enabled' | 'disabled' | 'enable-on-click';\n /**\n * A factory for curve generators addressing both lines and areas.\n *\n * @remarks\n *\n * Default: 'curveMonotoneX'\n */\n curve?: 'curveStepBefore' | 'curveMonotoneX';\n /**\n * Controls if the arrow heads should be rendered or not.\n *\n * Default: false\n */\n showArrowHeads?: boolean;\n /**\n * Controls if the graph should be contained or grow\n *\n * @remarks\n *\n * Default: 'grow'\n */\n fit?: 'grow' | 'contain';\n /**\n * Controls if user can toggle fullscreen mode\n *\n * @remarks\n *\n * Default: true\n */\n allowFullscreen?: boolean;\n}\n\nconst WORKSPACE_ID = 'workspace';\nconst DEPENDENCY_GRAPH_SVG = 'dependency-graph';\n\n/**\n * Graph component used to visualize relations between entities\n *\n * @public\n */\nexport function DependencyGraph<NodeData, EdgeData>(\n props: DependencyGraphProps<NodeData, EdgeData>,\n) {\n const {\n edges,\n nodes,\n renderNode,\n direction = Types.Direction.TOP_BOTTOM,\n align,\n nodeMargin = 50,\n edgeMargin = 10,\n rankMargin = 50,\n paddingX = 0,\n paddingY = 0,\n acyclicer,\n ranker = Types.Ranker.NETWORK_SIMPLEX,\n labelPosition = Types.LabelPosition.RIGHT,\n labelOffset = 10,\n edgeRanks = 1,\n edgeWeight = 1,\n renderEdge,\n renderLabel,\n defs,\n zoom = 'enabled',\n curve = 'curveMonotoneX',\n showArrowHeads = false,\n fit = 'grow',\n allowFullscreen = true,\n ...svgProps\n } = props;\n const theme = useTheme();\n const [containerWidth, setContainerWidth] = useState<number>(100);\n const [containerHeight, setContainerHeight] = useState<number>(100);\n const fullScreenHandle = useFullScreenHandle();\n const styles = useStyles();\n const { t } = useTranslationRef(coreComponentsTranslationRef);\n\n const graph = useRef<dagre.graphlib.Graph<Types.DependencyNode<NodeData>>>(\n new dagre.graphlib.Graph(),\n );\n const [graphWidth, setGraphWidth] = useState<number>(\n graph.current.graph()?.width || 0,\n );\n const [graphHeight, setGraphHeight] = useState<number>(\n graph.current.graph()?.height || 0,\n );\n const [graphNodes, setGraphNodes] = useState<string[]>([]);\n const [graphEdges, setGraphEdges] = useState<dagre.Edge[]>([]);\n\n const maxWidth = Math.max(graphWidth, containerWidth);\n const maxHeight = Math.max(graphHeight, containerHeight);\n\n const [_measureRef] = useMeasure();\n const measureRef = once(_measureRef);\n\n const scalableHeight =\n fit === 'grow' && !fullScreenHandle.active ? maxHeight : '100%';\n\n const containerRef = useMemo(\n () =>\n debounce((root: HTMLDivElement) => {\n if (!root) {\n return;\n }\n measureRef(root);\n\n // Set up zooming + panning\n const node: SVGSVGElement = root.querySelector(\n `svg#${DEPENDENCY_GRAPH_SVG}`,\n ) as SVGSVGElement;\n if (!node) {\n return;\n }\n const container = d3Selection.select<SVGSVGElement, null>(node);\n const workspace = d3Selection.select(node.getElementById(WORKSPACE_ID));\n\n function enableZoom() {\n container.call(\n d3Zoom\n .zoom<SVGSVGElement, null>()\n .scaleExtent([1, Infinity])\n .on('zoom', event => {\n event.transform.x = Math.min(\n 0,\n Math.max(\n event.transform.x,\n maxWidth - maxWidth * event.transform.k,\n ),\n );\n event.transform.y = Math.min(\n 0,\n Math.max(\n event.transform.y,\n maxHeight - maxHeight * event.transform.k,\n ),\n );\n workspace.attr('transform', event.transform);\n }),\n );\n }\n\n if (zoom === 'enabled') {\n enableZoom();\n } else if (zoom === 'enable-on-click') {\n container.on('click', () => enableZoom());\n }\n\n const { width: newContainerWidth, height: newContainerHeight } =\n root.getBoundingClientRect();\n if (\n containerWidth !== newContainerWidth &&\n newContainerWidth <= maxWidth\n ) {\n setContainerWidth(newContainerWidth);\n }\n if (\n containerHeight !== newContainerHeight &&\n newContainerHeight <= maxHeight\n ) {\n setContainerHeight(newContainerHeight);\n }\n }, 100),\n [measureRef, containerHeight, containerWidth, maxWidth, maxHeight, zoom],\n );\n\n const setNodesAndEdges = useCallback(() => {\n // Cleaning up lingering nodes and edges\n const currentGraphNodes = graph.current.nodes();\n const currentGraphEdges = graph.current.edges();\n\n currentGraphNodes.forEach(nodeId => {\n const remainingNode = nodes.some(node => node.id === nodeId);\n if (!remainingNode) {\n graph.current.removeNode(nodeId);\n }\n });\n\n currentGraphEdges.forEach(e => {\n const remainingEdge = edges.some(\n edge => edge.from === e.v && edge.to === e.w,\n );\n if (!remainingEdge) {\n graph.current.removeEdge(e.v, e.w);\n }\n });\n\n // Adding/updating nodes and edges\n nodes.forEach(node => {\n const existingNode = graph.current\n .nodes()\n .find(nodeId => node.id === nodeId);\n\n if (existingNode && graph.current.node(existingNode)) {\n const { width, height, x, y } = graph.current.node(existingNode);\n graph.current.setNode(existingNode, { ...node, width, height, x, y });\n } else {\n graph.current.setNode(node.id, { ...node, width: 0, height: 0 });\n }\n });\n\n edges.forEach(e => {\n graph.current.setEdge(e.from, e.to, {\n ...e,\n label: e.label,\n width: 0,\n height: 0,\n labelpos: labelPosition,\n labeloffset: labelOffset,\n weight: edgeWeight,\n minlen: edgeRanks,\n });\n });\n }, [edges, nodes, labelPosition, labelOffset, edgeWeight, edgeRanks]);\n\n const updateGraph = useMemo(\n () =>\n debounce(\n () => {\n dagre.layout(graph.current);\n const { height, width } = graph.current.graph();\n const newHeight = Math.max(0, height || 0);\n const newWidth = Math.max(0, width || 0);\n setGraphWidth(newWidth);\n setGraphHeight(newHeight);\n\n setGraphNodes(graph.current.nodes());\n setGraphEdges(graph.current.edges());\n },\n 250,\n { leading: true },\n ),\n [],\n );\n\n useEffect(() => {\n graph.current.setGraph({\n rankdir: direction,\n align,\n nodesep: nodeMargin,\n edgesep: edgeMargin,\n ranksep: rankMargin,\n marginx: paddingX,\n marginy: paddingY,\n acyclicer,\n ranker,\n });\n\n setNodesAndEdges();\n updateGraph();\n\n return updateGraph.cancel;\n }, [\n acyclicer,\n align,\n direction,\n edgeMargin,\n paddingX,\n paddingY,\n nodeMargin,\n rankMargin,\n ranker,\n setNodesAndEdges,\n updateGraph,\n ]);\n\n const setNode = useCallback(\n (id: string, node: Types.DependencyNode<NodeData>) => {\n graph.current.setNode(id, node);\n updateGraph();\n return graph.current;\n },\n [updateGraph],\n );\n\n const setEdge = useCallback(\n (id: dagre.Edge, edge: Types.DependencyEdge<EdgeData>) => {\n graph.current.setEdge(id, edge);\n updateGraph();\n return graph.current;\n },\n [updateGraph],\n );\n\n return (\n <FullScreen\n handle={fullScreenHandle}\n className={classNames(\n fullScreenHandle.active ? styles.fullscreen : styles.root,\n )}\n >\n {allowFullscreen && (\n <Tooltip title={t('dependencyGraph.fullscreenTooltip')}>\n <IconButton\n className={styles.fullscreenButton}\n onClick={\n fullScreenHandle.active\n ? fullScreenHandle.exit\n : fullScreenHandle.enter\n }\n >\n {fullScreenHandle.active ? (\n <FullscreenExitIcon />\n ) : (\n <FullscreenIcon />\n )}\n </IconButton>\n </Tooltip>\n )}\n\n <div ref={containerRef} style={{ width: '100%', height: '100%' }}>\n <svg\n {...svgProps}\n width=\"100%\"\n height={scalableHeight}\n viewBox={`0 0 ${maxWidth} ${maxHeight}`}\n id={DEPENDENCY_GRAPH_SVG}\n >\n <defs>\n <marker\n id={ARROW_MARKER_ID}\n viewBox=\"0 0 24 24\"\n markerWidth=\"14\"\n markerHeight=\"14\"\n refX=\"16\"\n refY=\"12\"\n orient=\"auto\"\n markerUnits=\"strokeWidth\"\n >\n <path\n fill={theme.palette.textSubtle}\n d=\"M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z\"\n />\n </marker>\n {defs}\n </defs>\n <g id={WORKSPACE_ID}>\n <svg\n width={graphWidth}\n height={graphHeight}\n y={maxHeight / 2 - graphHeight / 2}\n x={maxWidth / 2 - graphWidth / 2}\n viewBox={`0 0 ${graphWidth} ${graphHeight}`}\n >\n {graphEdges.map(e => {\n const edge = graph.current.edge(e) as GraphEdge<EdgeData>;\n if (!edge) return null;\n if (renderEdge) return renderEdge({ edge, id: e });\n\n return (\n <Edge\n key={`${e.v}-${e.w}`}\n id={e}\n setEdge={setEdge}\n render={renderLabel}\n edge={edge}\n curve={curve}\n showArrowHeads={showArrowHeads}\n />\n );\n })}\n {graphNodes.map((id: string) => {\n const node = graph.current.node(id);\n if (!node) return null;\n return (\n <Node\n key={id}\n setNode={setNode}\n render={renderNode}\n node={node}\n />\n );\n })}\n </svg>\n </g>\n </svg>\n </div>\n </FullScreen>\n );\n}\n"],"names":["Types"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA6CA,MAAM,SAAA,GAAY,UAAA,CAAW,CAAC,KAAA,MAAkB;AAAA,EAC9C,gBAAA,EAAkB;AAAA,IAChB,QAAA,EAAU,UAAA;AAAA,IACV,KAAA,EAAO;AAAA,GACT;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,QAAA,EAAU,QAAA;AAAA,IACV,SAAA,EAAW,MAAA;AAAA,IACX,QAAA,EAAU;AAAA,GACZ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,SAAA,EAAW;AAAA,GACb;AAAA,EACA,UAAA,EAAY;AAAA,IACV,eAAA,EAAiB,KAAA,CAAM,OAAA,CAAQ,UAAA,CAAW;AAAA;AAE9C,CAAA,CAAE,CAAA;AAqKF,MAAM,YAAA,GAAe,WAAA;AACrB,MAAM,oBAAA,GAAuB,kBAAA;AAOtB,SAAS,gBACd,KAAA,EACA;AACA,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,KAAA;AAAA,IACA,UAAA;AAAA,IACA,SAAA,GAAYA,qBAAM,SAAA,CAAU,UAAA;AAAA,IAC5B,KAAA;AAAA,IACA,UAAA,GAAa,EAAA;AAAA,IACb,UAAA,GAAa,EAAA;AAAA,IACb,UAAA,GAAa,EAAA;AAAA,IACb,QAAA,GAAW,CAAA;AAAA,IACX,QAAA,GAAW,CAAA;AAAA,IACX,SAAA;AAAA,IACA,MAAA,GAASA,qBAAM,MAAA,CAAO,eAAA;AAAA,IACtB,aAAA,GAAgBA,qBAAM,aAAA,CAAc,KAAA;AAAA,IACpC,WAAA,GAAc,EAAA;AAAA,IACd,SAAA,GAAY,CAAA;AAAA,IACZ,UAAA,GAAa,CAAA;AAAA,IACb,UAAA;AAAA,IACA,WAAA;AAAA,IACA,IAAA;AAAA,IACA,IAAA,GAAO,SAAA;AAAA,IACP,KAAA,GAAQ,gBAAA;AAAA,IACR,cAAA,GAAiB,KAAA;AAAA,IACjB,GAAA,GAAM,MAAA;AAAA,IACN,eAAA,GAAkB,IAAA;AAAA,IAClB,GAAG;AAAA,GACL,GAAI,KAAA;AACJ,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAAiB,GAAG,CAAA;AAChE,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAAiB,GAAG,CAAA;AAClE,EAAA,MAAM,mBAAmB,mBAAA,EAAoB;AAC7C,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,MAAM,EAAE,CAAA,EAAE,GAAI,iBAAA,CAAkB,4BAA4B,CAAA;AAE5D,EAAA,MAAM,KAAA,GAAQ,MAAA;AAAA,IACZ,IAAI,KAAA,CAAM,QAAA,CAAS,KAAA;AAAM,GAC3B;AACA,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,QAAA;AAAA,IAClC,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAM,EAAG,KAAA,IAAS;AAAA,GAClC;AACA,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,QAAA;AAAA,IACpC,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAM,EAAG,MAAA,IAAU;AAAA,GACnC;AACA,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,QAAA,CAAmB,EAAE,CAAA;AACzD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,QAAA,CAAuB,EAAE,CAAA;AAE7D,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,UAAA,EAAY,cAAc,CAAA;AACpD,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,eAAe,CAAA;AAEvD,EAAA,MAAM,CAAC,WAAW,CAAA,GAAI,UAAA,EAAW;AACjC,EAAA,MAAM,UAAA,GAAa,KAAK,WAAW,CAAA;AAEnC,EAAA,MAAM,iBACJ,GAAA,KAAQ,MAAA,IAAU,CAAC,gBAAA,CAAiB,SAAS,SAAA,GAAY,MAAA;AAE3D,EAAA,MAAM,YAAA,GAAe,OAAA;AAAA,IACnB,MACE,QAAA,CAAS,CAAC,IAAA,KAAyB;AACjC,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA;AAAA,MACF;AACA,MAAA,UAAA,CAAW,IAAI,CAAA;AAGf,MAAA,MAAM,OAAsB,IAAA,CAAK,aAAA;AAAA,QAC/B,OAAO,oBAAoB,CAAA;AAAA,OAC7B;AACA,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA;AAAA,MACF;AACA,MAAA,MAAM,SAAA,GAAY,WAAA,CAAY,MAAA,CAA4B,IAAI,CAAA;AAC9D,MAAA,MAAM,YAAY,WAAA,CAAY,MAAA,CAAO,IAAA,CAAK,cAAA,CAAe,YAAY,CAAC,CAAA;AAEtE,MAAA,SAAS,UAAA,GAAa;AACpB,QAAA,SAAA,CAAU,IAAA;AAAA,UACR,MAAA,CACG,IAAA,EAA0B,CAC1B,WAAA,CAAY,CAAC,CAAA,EAAG,QAAQ,CAAC,CAAA,CACzB,EAAA,CAAG,MAAA,EAAQ,CAAA,KAAA,KAAS;AACnB,YAAA,KAAA,CAAM,SAAA,CAAU,IAAI,IAAA,CAAK,GAAA;AAAA,cACvB,CAAA;AAAA,cACA,IAAA,CAAK,GAAA;AAAA,gBACH,MAAM,SAAA,CAAU,CAAA;AAAA,gBAChB,QAAA,GAAW,QAAA,GAAW,KAAA,CAAM,SAAA,CAAU;AAAA;AACxC,aACF;AACA,YAAA,KAAA,CAAM,SAAA,CAAU,IAAI,IAAA,CAAK,GAAA;AAAA,cACvB,CAAA;AAAA,cACA,IAAA,CAAK,GAAA;AAAA,gBACH,MAAM,SAAA,CAAU,CAAA;AAAA,gBAChB,SAAA,GAAY,SAAA,GAAY,KAAA,CAAM,SAAA,CAAU;AAAA;AAC1C,aACF;AACA,YAAA,SAAA,CAAU,IAAA,CAAK,WAAA,EAAa,KAAA,CAAM,SAAS,CAAA;AAAA,UAC7C,CAAC;AAAA,SACL;AAAA,MACF;AAEA,MAAA,IAAI,SAAS,SAAA,EAAW;AACtB,QAAA,UAAA,EAAW;AAAA,MACb,CAAA,MAAA,IAAW,SAAS,iBAAA,EAAmB;AACrC,QAAA,SAAA,CAAU,EAAA,CAAG,OAAA,EAAS,MAAM,UAAA,EAAY,CAAA;AAAA,MAC1C;AAEA,MAAA,MAAM,EAAE,KAAA,EAAO,iBAAA,EAAmB,QAAQ,kBAAA,EAAmB,GAC3D,KAAK,qBAAA,EAAsB;AAC7B,MAAA,IACE,cAAA,KAAmB,iBAAA,IACnB,iBAAA,IAAqB,QAAA,EACrB;AACA,QAAA,iBAAA,CAAkB,iBAAiB,CAAA;AAAA,MACrC;AACA,MAAA,IACE,eAAA,KAAoB,kBAAA,IACpB,kBAAA,IAAsB,SAAA,EACtB;AACA,QAAA,kBAAA,CAAmB,kBAAkB,CAAA;AAAA,MACvC;AAAA,IACF,GAAG,GAAG,CAAA;AAAA,IACR,CAAC,UAAA,EAAY,eAAA,EAAiB,cAAA,EAAgB,QAAA,EAAU,WAAW,IAAI;AAAA,GACzE;AAEA,EAAA,MAAM,gBAAA,GAAmB,YAAY,MAAM;AAEzC,IAAA,MAAM,iBAAA,GAAoB,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAM;AAC9C,IAAA,MAAM,iBAAA,GAAoB,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAM;AAE9C,IAAA,iBAAA,CAAkB,QAAQ,CAAA,MAAA,KAAU;AAClC,MAAA,MAAM,gBAAgB,KAAA,CAAM,IAAA,CAAK,CAAA,IAAA,KAAQ,IAAA,CAAK,OAAO,MAAM,CAAA;AAC3D,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA,KAAA,CAAM,OAAA,CAAQ,WAAW,MAAM,CAAA;AAAA,MACjC;AAAA,IACF,CAAC,CAAA;AAED,IAAA,iBAAA,CAAkB,QAAQ,CAAA,CAAA,KAAK;AAC7B,MAAA,MAAM,gBAAgB,KAAA,CAAM,IAAA;AAAA,QAC1B,UAAQ,IAAA,CAAK,IAAA,KAAS,EAAE,CAAA,IAAK,IAAA,CAAK,OAAO,CAAA,CAAE;AAAA,OAC7C;AACA,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA,KAAA,CAAM,OAAA,CAAQ,UAAA,CAAW,CAAA,CAAE,CAAA,EAAG,EAAE,CAAC,CAAA;AAAA,MACnC;AAAA,IACF,CAAC,CAAA;AAGD,IAAA,KAAA,CAAM,QAAQ,CAAA,IAAA,KAAQ;AACpB,MAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CACxB,KAAA,GACA,IAAA,CAAK,CAAA,MAAA,KAAU,IAAA,CAAK,EAAA,KAAO,MAAM,CAAA;AAEpC,MAAA,IAAI,YAAA,IAAgB,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,YAAY,CAAA,EAAG;AACpD,QAAA,MAAM,EAAE,OAAO,MAAA,EAAQ,CAAA,EAAG,GAAE,GAAI,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,YAAY,CAAA;AAC/D,QAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,YAAA,EAAc,EAAE,GAAG,MAAM,KAAA,EAAO,MAAA,EAAQ,CAAA,EAAG,CAAA,EAAG,CAAA;AAAA,MACtE,CAAA,MAAO;AACL,QAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,EAAA,EAAI,EAAE,GAAG,IAAA,EAAM,KAAA,EAAO,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,CAAA;AAAA,MACjE;AAAA,IACF,CAAC,CAAA;AAED,IAAA,KAAA,CAAM,QAAQ,CAAA,CAAA,KAAK;AACjB,MAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,CAAA,CAAE,IAAA,EAAM,EAAE,EAAA,EAAI;AAAA,QAClC,GAAG,CAAA;AAAA,QACH,OAAO,CAAA,CAAE,KAAA;AAAA,QACT,KAAA,EAAO,CAAA;AAAA,QACP,MAAA,EAAQ,CAAA;AAAA,QACR,QAAA,EAAU,aAAA;AAAA,QACV,WAAA,EAAa,WAAA;AAAA,QACb,MAAA,EAAQ,UAAA;AAAA,QACR,MAAA,EAAQ;AAAA,OACT,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,KAAA,EAAO,KAAA,EAAO,eAAe,WAAA,EAAa,UAAA,EAAY,SAAS,CAAC,CAAA;AAEpE,EAAA,MAAM,WAAA,GAAc,OAAA;AAAA,IAClB,MACE,QAAA;AAAA,MACE,MAAM;AACJ,QAAA,KAAA,CAAM,MAAA,CAAO,MAAM,OAAO,CAAA;AAC1B,QAAA,MAAM,EAAE,MAAA,EAAQ,KAAA,EAAM,GAAI,KAAA,CAAM,QAAQ,KAAA,EAAM;AAC9C,QAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,UAAU,CAAC,CAAA;AACzC,QAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAS,CAAC,CAAA;AACvC,QAAA,aAAA,CAAc,QAAQ,CAAA;AACtB,QAAA,cAAA,CAAe,SAAS,CAAA;AAExB,QAAA,aAAA,CAAc,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,CAAA;AACnC,QAAA,aAAA,CAAc,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,CAAA;AAAA,MACrC,CAAA;AAAA,MACA,GAAA;AAAA,MACA,EAAE,SAAS,IAAA;AAAK,KAClB;AAAA,IACF;AAAC,GACH;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,KAAA,CAAM,QAAQ,QAAA,CAAS;AAAA,MACrB,OAAA,EAAS,SAAA;AAAA,MACT,KAAA;AAAA,MACA,OAAA,EAAS,UAAA;AAAA,MACT,OAAA,EAAS,UAAA;AAAA,MACT,OAAA,EAAS,UAAA;AAAA,MACT,OAAA,EAAS,QAAA;AAAA,MACT,OAAA,EAAS,QAAA;AAAA,MACT,SAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,gBAAA,EAAiB;AACjB,IAAA,WAAA,EAAY;AAEZ,IAAA,OAAO,WAAA,CAAY,MAAA;AAAA,EACrB,CAAA,EAAG;AAAA,IACD,SAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,MAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,IAAY,IAAA,KAAyC;AACpD,MAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,EAAA,EAAI,IAAI,CAAA;AAC9B,MAAA,WAAA,EAAY;AACZ,MAAA,OAAO,KAAA,CAAM,OAAA;AAAA,IACf,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,IAAgB,IAAA,KAAyC;AACxD,MAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,EAAA,EAAI,IAAI,CAAA;AAC9B,MAAA,WAAA,EAAY;AACZ,MAAA,OAAO,KAAA,CAAM,OAAA;AAAA,IACf,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,uBACE,IAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,MAAA,EAAQ,gBAAA;AAAA,MACR,SAAA,EAAW,UAAA;AAAA,QACT,gBAAA,CAAiB,MAAA,GAAS,MAAA,CAAO,UAAA,GAAa,MAAA,CAAO;AAAA,OACvD;AAAA,MAEC,QAAA,EAAA;AAAA,QAAA,eAAA,oBACC,GAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAO,CAAA,CAAE,mCAAmC,CAAA,EACnD,QAAA,kBAAA,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,WAAW,MAAA,CAAO,gBAAA;AAAA,YAClB,OAAA,EACE,gBAAA,CAAiB,MAAA,GACb,gBAAA,CAAiB,OACjB,gBAAA,CAAiB,KAAA;AAAA,YAGtB,2BAAiB,MAAA,mBAChB,GAAA,CAAC,kBAAA,EAAA,EAAmB,CAAA,uBAEnB,cAAA,EAAA,EAAe;AAAA;AAAA,SAEpB,EACF,CAAA;AAAA,wBAGF,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,YAAA,EAAc,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAO,EAC7D,QAAA,kBAAA,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACE,GAAG,QAAA;AAAA,YACJ,KAAA,EAAM,MAAA;AAAA,YACN,MAAA,EAAQ,cAAA;AAAA,YACR,OAAA,EAAS,CAAA,IAAA,EAAO,QAAQ,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAAA,YACrC,EAAA,EAAI,oBAAA;AAAA,YAEJ,QAAA,EAAA;AAAA,8BAAA,IAAA,CAAC,MAAA,EAAA,EACC,QAAA,EAAA;AAAA,gCAAA,GAAA;AAAA,kBAAC,QAAA;AAAA,kBAAA;AAAA,oBACC,EAAA,EAAI,eAAA;AAAA,oBACJ,OAAA,EAAQ,WAAA;AAAA,oBACR,WAAA,EAAY,IAAA;AAAA,oBACZ,YAAA,EAAa,IAAA;AAAA,oBACb,IAAA,EAAK,IAAA;AAAA,oBACL,IAAA,EAAK,IAAA;AAAA,oBACL,MAAA,EAAO,MAAA;AAAA,oBACP,WAAA,EAAY,aAAA;AAAA,oBAEZ,QAAA,kBAAA,GAAA;AAAA,sBAAC,MAAA;AAAA,sBAAA;AAAA,wBACC,IAAA,EAAM,MAAM,OAAA,CAAQ,UAAA;AAAA,wBACpB,CAAA,EAAE;AAAA;AAAA;AACJ;AAAA,iBACF;AAAA,gBACC;AAAA,eAAA,EACH,CAAA;AAAA,8BACA,GAAA,CAAC,GAAA,EAAA,EAAE,EAAA,EAAI,YAAA,EACL,QAAA,kBAAA,IAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,KAAA,EAAO,UAAA;AAAA,kBACP,MAAA,EAAQ,WAAA;AAAA,kBACR,CAAA,EAAG,SAAA,GAAY,CAAA,GAAI,WAAA,GAAc,CAAA;AAAA,kBACjC,CAAA,EAAG,QAAA,GAAW,CAAA,GAAI,UAAA,GAAa,CAAA;AAAA,kBAC/B,OAAA,EAAS,CAAA,IAAA,EAAO,UAAU,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA;AAAA,kBAExC,QAAA,EAAA;AAAA,oBAAA,UAAA,CAAW,IAAI,CAAA,CAAA,KAAK;AACnB,sBAAA,MAAM,IAAA,GAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA;AACjC,sBAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,sBAAA,IAAI,YAAY,OAAO,UAAA,CAAW,EAAE,IAAA,EAAM,EAAA,EAAI,GAAG,CAAA;AAEjD,sBAAA,uBACE,GAAA;AAAA,wBAAC,IAAA;AAAA,wBAAA;AAAA,0BAEC,EAAA,EAAI,CAAA;AAAA,0BACJ,OAAA;AAAA,0BACA,MAAA,EAAQ,WAAA;AAAA,0BACR,IAAA;AAAA,0BACA,KAAA;AAAA,0BACA;AAAA,yBAAA;AAAA,wBANK,CAAA,EAAG,CAAA,CAAE,CAAC,CAAA,CAAA,EAAI,EAAE,CAAC,CAAA;AAAA,uBAOpB;AAAA,oBAEJ,CAAC,CAAA;AAAA,oBACA,UAAA,CAAW,GAAA,CAAI,CAAC,EAAA,KAAe;AAC9B,sBAAA,MAAM,IAAA,GAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAA;AAClC,sBAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,sBAAA,uBACE,GAAA;AAAA,wBAAC,IAAA;AAAA,wBAAA;AAAA,0BAEC,OAAA;AAAA,0BACA,MAAA,EAAQ,UAAA;AAAA,0BACR;AAAA,yBAAA;AAAA,wBAHK;AAAA,uBAIP;AAAA,oBAEJ,CAAC;AAAA;AAAA;AAAA,eACH,EACF;AAAA;AAAA;AAAA,SACF,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
package/dist/index.d.ts CHANGED
@@ -236,6 +236,7 @@ interface CodeSnippetProps {
236
236
  */
237
237
  customStyle?: any;
238
238
  }
239
+
239
240
  /**
240
241
  * Thin wrapper on top of {@link https://react-syntax-highlighter.github.io/react-syntax-highlighter/ | react-syntax-highlighter}
241
242
  * providing consistent theming and copy code button
@@ -713,6 +714,7 @@ interface DependencyGraphProps<NodeData, EdgeData> extends SVGProps<SVGSVGElemen
713
714
  */
714
715
  allowFullscreen?: boolean;
715
716
  }
717
+
716
718
  /**
717
719
  * Graph component used to visualize relations between entities
718
720
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/core-components",
3
- "version": "0.18.11-next.0",
3
+ "version": "0.18.11-next.2",
4
4
  "description": "Core components used by Backstage plugins and apps",
5
5
  "backstage": {
6
6
  "role": "web-library"
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@backstage/config": "1.3.8",
70
- "@backstage/core-plugin-api": "1.12.6",
70
+ "@backstage/core-plugin-api": "1.12.7-next.0",
71
71
  "@backstage/errors": "1.3.1",
72
72
  "@backstage/theme": "0.7.3",
73
73
  "@backstage/version-bridge": "1.0.12",
@@ -110,10 +110,10 @@
110
110
  "zod": "^3.25.76 || ^4.0.0"
111
111
  },
112
112
  "devDependencies": {
113
- "@backstage/app-defaults": "1.7.9-next.0",
114
- "@backstage/cli": "0.36.3-next.0",
115
- "@backstage/core-app-api": "1.20.1",
116
- "@backstage/test-utils": "1.7.18",
113
+ "@backstage/app-defaults": "1.7.9-next.1",
114
+ "@backstage/cli": "0.36.3-next.1",
115
+ "@backstage/core-app-api": "1.20.2-next.0",
116
+ "@backstage/test-utils": "1.7.19-next.0",
117
117
  "@testing-library/dom": "^10.0.0",
118
118
  "@testing-library/jest-dom": "^6.0.0",
119
119
  "@testing-library/user-event": "^14.0.0",