@backstage/core-components 0.17.1 → 0.17.2-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @backstage/core-components
2
2
 
3
+ ## 0.17.2-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - e0d1025: `LogViewer` now supports a `textWrap` prop that wraps log lines to the next line for overflowing content instead of using horizontal scroll
8
+ - bb84534: Fix the hidden sidebar's sub-menu when the sidebar is scrollable
9
+ - 72d019d: Removed various typos
10
+ - Updated dependencies
11
+ - @backstage/theme@0.6.6-next.0
12
+ - @backstage/config@1.3.2
13
+ - @backstage/core-plugin-api@1.10.6
14
+ - @backstage/errors@1.2.7
15
+ - @backstage/version-bridge@1.0.11
16
+
3
17
  ## 0.17.1
4
18
 
5
19
  ### Patch Changes
@@ -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 * defauls 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":";;;;;;;;AAsFA,MAAM,wBAAwB,CAAC;AAAA,EAC7B,kBAAA;AAAA,EACA,MAAA;AAAA,EACA,eAAA;AAAA,EACA,oBAAA;AAAA,EACA,uBAAA;AAAA,EACA,UAAA;AAAA,EACA,aAAA;AAAA,EACA,sBAAA;AAAA,EACA,yBAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAA4C,KAAA;AAC1C,EAAA,MAAM,yBAAyB,uBAA0B,GAAA,GAAA;AACzD,EAAM,MAAA,gBAAA,GAAmB,sBAAyB,GAAA,CAAA,GAAI,IAAO,GAAA,KAAA;AAE7D,EAAA,MAAM,WAAW,YAAY;AAK3B,IAAA,aAAA,CAAc,IAAI,CAAA;AAClB,IAAA,yBAAA,CAA0B,sBAAsB,CAAA;AAAA,GAClD;AAEA,EAAA,MAAM,SAAS,MAAM;AAGnB,IAAA,aAAA,CAAc,KAAK,CAAA;AACnB,IAAA,yBAAA,CAA0B,CAAC,CAAA;AAC3B,IAAA,WAAA,CAAY,OAAQ,EAAA;AAAA,GACtB;AAEA,EAAA,MAAM,WAAW,MAAM;AAIrB,IAAA,aAAA,CAAc,KAAK,CAAA;AACnB,IAAA,yBAAA,CAA0B,CAAC,CAAA;AAAA,GAC7B;AAEA,EAAM,MAAA,QAAA,GAAW,CACf,MAAA,EACA,UACG,KAAA;AAGH,IAAoB,mBAAA,CAAA,IAAA,iBAAS,IAAA,IAAA,EAAM,CAAA;AAAA,GACrC;AAEA,EAAA,MAAM,QAAQ,YAAa,CAAA;AAAA,IACzB,OAAA,EAAS,qBAAqB,EAAK,GAAA,GAAA;AAAA,IACnC,MAAA;AAAA,IACA,QAAU,EAAA,IAAA;AAAA,IACV,IAAM,EAAA,kBAAA;AAAA,IACN,MAAA,EAAQ,kBAAkB,YAAe,GAAA,KAAA,CAAA;AAAA,IACzC,MAAA;AAAA,IACA,QAAA,EAAU,mBAAmB,QAAW,GAAA,KAAA,CAAA;AAAA,IACxC,QAAA,EAAU,uBAAuB,QAAW,GAAA,KAAA,CAAA;AAAA,IAC5C,QAAA,EAAU,mBAAmB,QAAW,GAAA,KAAA,CAAA;AAAA,IACxC,gBAAA,EAAkB,mBAAmB,sBAAyB,GAAA,KAAA,CAAA;AAAA,IAC9D,UAAY,EAAA;AAAA,GACb,CAAA;AAED,EAAA,uCAEK,QACC,EAAA,gBAAA,oBAAA,GAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MACC,SAAW,EAAA,KAAA;AAAA,MACX,IAAM,EAAA,UAAA;AAAA,MACN,OAAS,EAAA,aAAA;AAAA,MACT,aAAe,EAAA,sBAAA;AAAA,MACf,gBAAkB,EAAA,yBAAA;AAAA,MAClB,mBAAqB,EAAA;AAAA;AAAA,GAG3B,EAAA,CAAA;AAEJ,CAAA;AAEA,MAAM,aAA2C,GAAA;AAAA,EAC/C,OAAS,EAAA,IAAA;AAAA,EACT,kBAAoB,EAAA,GAAA;AAAA,EACpB,uBAAyB,EAAA,EAAA;AAAA,EACzB,eAAiB,EAAA,IAAA;AAAA,EACjB,oBAAsB,EAAA;AACxB,CAAA;AAKA,MAAM,oBAAmD,GAAA;AAAA,EACvD,WAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,gBAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA;AACF,CAAA;AAMA,MAAM,WAAA,GAAc,CAClB,SAAA,EACA,KAC8B,KAAA;AAC9B,EAAO,OAAA;AAAA,IACL,SACE,SAAU,CAAA,kBAAA,CAAmB,yBAAyB,CACtD,IAAA,KAAA,CAAM,WACN,aAAc,CAAA,OAAA;AAAA,IAChB,oBACE,SAAU,CAAA,iBAAA,CAAkB,oCAAoC,CAChE,IAAA,KAAA,CAAM,sBACN,aAAc,CAAA,kBAAA;AAAA,IAChB,yBACE,SAAU,CAAA,iBAAA,CAAkB,yCAAyC,CACrE,IAAA,KAAA,CAAM,2BACN,aAAc,CAAA,uBAAA;AAAA,IAChB,iBACE,SAAU,CAAA,kBAAA,CAAmB,iCAAiC,CAC9D,IAAA,KAAA,CAAM,mBACN,aAAc,CAAA,eAAA;AAAA,IAChB,sBACE,SAAU,CAAA,kBAAA,CAAmB,sCAAsC,CACnE,IAAA,KAAA,CAAM,wBACN,aAAc,CAAA;AAAA,GAClB;AACF,CAAA;AASa,MAAA,UAAA,GAAa,CAAC,KAA+C,KAAA;AACxE,EAAM,MAAA,WAAA,GAAc,OAAO,cAAc,CAAA;AACzC,EAAM,MAAA,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,KAAK,CAAA;AAC9C,EAAA,SAAA,CAAU,MAAM;AAEd,IAAA,eAAe,WAAW,QAAuB,EAAA;AAC/C,MAAA,IAAA,CAAK,MAAM,QAAA,CAAS,cAAe,EAAA,EAAG,KAAO,EAAA;AAC3C,QAAA,WAAA,CAAY,IAAI,CAAA;AAAA,OACX,MAAA;AACL,QAAA,WAAA,CAAY,KAAK,CAAA;AAAA;AACnB;AAEF,IAAA,UAAA,CAAW,WAAW,CAAA;AAAA,GACxB,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,EAAM,MAAA;AAAA,IACJ,OAAA;AAAA,IACA,kBAAA;AAAA,IACA,uBAAA;AAAA,IACA,oBAAA;AAAA,IACA;AAAA,GACF,GAAqB,QAAQ,MAAM;AACjC,IAAO,OAAA,WAAA,CAAY,WAAW,KAAK,CAAA;AAAA,GAClC,EAAA,CAAC,SAAW,EAAA,KAAK,CAAC,CAAA;AAErB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,qBAAqB,GAAK,EAAA;AAC5B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA;AAGF,IAAA,IAAI,0BAA0B,CAAG,EAAA;AAC/B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA;AAGF,IAAI,IAAA,kBAAA,GAAqB,MAAM,uBAAyB,EAAA;AACtD,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,wEAAA;AAAA,OACF;AAAA;AACF,GACC,EAAA,CAAC,kBAAoB,EAAA,uBAAuB,CAAC,CAAA;AAEhD,EAAA,MAAM,mBAAsC,GAAA,OAAA;AAAA,IAC1C,MAAM,IAAI,qBAAA,CAAsB,4BAA4B,CAAA;AAAA,IAC5D;AAAC,GACH;AACA,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAkB,KAAK,CAAA;AAE3D,EAAA,MAAM,CAAC,sBAAA,EAAwB,yBAAyB,CAAA,GACtD,SAAiB,CAAC,CAAA;AAEpB,EAAgC,+BAAA,CAAA;AAAA,IAC9B,YAAc,EAAA,oBAAA;AAAA,IACd,qBAAqB,OAAW,IAAA,QAAA;AAAA,IAChC,oBAAoB,kBAAqB,GAAA,EAAA;AAAA,IACzC,mBAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAI,IAAA,CAAC,OAAW,IAAA,CAAC,QAAU,EAAA;AACzB,IAAO,OAAA,IAAA;AAAA;AAGT,EACE,uBAAA,GAAA;AAAA,IAAC,qBAAA;AAAA,IAAA;AAAA,MACC,kBAAA;AAAA,MACA,uBAAA;AAAA,MACA,eAAA;AAAA,MACA,MAAQ,EAAA,oBAAA;AAAA,MACR,oBAAA;AAAA,MACA,UAAA;AAAA,MACA,aAAA;AAAA,MACA,sBAAA;AAAA,MACA,yBAAA;AAAA,MACA,WAAA;AAAA,MACA;AAAA;AAAA,GACF;AAEJ;;;;"}
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,5 +1,5 @@
1
- import { jsx, Fragment } from 'react/jsx-runtime';
2
- import { useMemo } from 'react';
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useRef, useMemo, useEffect } from 'react';
3
3
  import startCase from 'lodash/startCase';
4
4
  import classNames from 'classnames';
5
5
  import Linkify from 'linkify-react';
@@ -110,12 +110,19 @@ function LogLine({
110
110
  line,
111
111
  classes,
112
112
  searchText,
113
- highlightResultIndex
113
+ highlightResultIndex,
114
+ setRowHeight
114
115
  }) {
116
+ const lineRef = useRef(null);
115
117
  const chunks = useMemo(
116
118
  () => calculateHighlightedChunks(line, searchText),
117
119
  [line, searchText]
118
120
  );
121
+ useEffect(() => {
122
+ if (lineRef.current && setRowHeight) {
123
+ setRowHeight(line.lineNumber, lineRef.current.offsetHeight);
124
+ }
125
+ }, [line.lineNumber, setRowHeight]);
119
126
  const elements = useMemo(
120
127
  () => chunks.map(({ text, modifiers, highlight }, index) => (
121
128
  // eslint-disable-next-line react/forbid-elements
@@ -124,16 +131,17 @@ function LogLine({
124
131
  {
125
132
  className: classNames(
126
133
  getModifierClasses(classes, modifiers),
127
- highlight !== void 0 && (highlight === highlightResultIndex ? classes.textSelectedHighlight : classes.textHighlight)
134
+ highlight !== void 0 && (highlight === highlightResultIndex ? classes.textSelectedHighlight : classes.textHighlight),
135
+ { [classes.textWrap]: !!setRowHeight }
128
136
  ),
129
137
  children: /* @__PURE__ */ jsx(Linkify, { options: { render: renderLink }, children: text })
130
138
  },
131
139
  index
132
140
  )
133
141
  )),
134
- [chunks, highlightResultIndex, classes]
142
+ [chunks, highlightResultIndex, classes, setRowHeight]
135
143
  );
136
- return /* @__PURE__ */ jsx(Fragment, { children: elements });
144
+ return /* @__PURE__ */ jsx("span", { ref: lineRef, children: elements });
137
145
  }
138
146
 
139
147
  export { LogLine, calculateHighlightedChunks, findSearchResults, getModifierClasses };
@@ -1 +1 @@
1
- {"version":3,"file":"LogLine.esm.js","sources":["../../../src/components/LogViewer/LogLine.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 { useMemo } from 'react';\nimport { AnsiChunk, AnsiLine, ChunkModifiers } from './AnsiProcessor';\nimport startCase from 'lodash/startCase';\nimport classnames from 'classnames';\nimport { useStyles } from './styles';\nimport Linkify from 'linkify-react';\nimport { Link } from '../Link';\n\nexport function getModifierClasses(\n classes: ReturnType<typeof useStyles>,\n modifiers: ChunkModifiers,\n) {\n const classNames = new Array<string>();\n if (modifiers.bold) {\n classNames.push(classes.modifierBold);\n }\n if (modifiers.italic) {\n classNames.push(classes.modifierItalic);\n }\n if (modifiers.underline) {\n classNames.push(classes.modifierUnderline);\n }\n if (modifiers.foreground) {\n const key = `modifierForeground${startCase(\n modifiers.foreground,\n )}` as keyof typeof classes;\n classNames.push(classes[key]);\n }\n if (modifiers.background) {\n const key = `modifierBackground${startCase(\n modifiers.background,\n )}` as keyof typeof classes;\n classNames.push(classes[key]);\n }\n return classNames.length > 0 ? classNames.join(' ') : undefined;\n}\n\nexport function findSearchResults(text: string, searchText: string) {\n if (!searchText || !text.includes(searchText)) {\n return undefined;\n }\n const searchResults = new Array<{ start: number; end: number }>();\n let offset = 0;\n for (;;) {\n const start = text.indexOf(searchText, offset);\n if (start === -1) {\n break;\n }\n const end = start + searchText.length;\n searchResults.push({ start, end });\n offset = end;\n }\n return searchResults;\n}\n\nexport interface HighlightAnsiChunk extends AnsiChunk {\n highlight?: number;\n}\n\nexport function calculateHighlightedChunks(\n line: AnsiLine,\n searchText: string,\n): HighlightAnsiChunk[] {\n const results = findSearchResults(line.text, searchText);\n if (!results) {\n return line.chunks;\n }\n\n const chunks = new Array<HighlightAnsiChunk>();\n\n let lineOffset = 0;\n let resultIndex = 0;\n let result = results[resultIndex];\n for (const chunk of line.chunks) {\n const { text, modifiers } = chunk;\n if (!result || lineOffset + text.length < result.start) {\n chunks.push(chunk);\n lineOffset += text.length;\n continue;\n }\n\n let localOffset = 0;\n while (result) {\n const localStart = Math.max(result.start - lineOffset, 0);\n if (localStart > text.length) {\n break; // The next result is not in this chunk\n }\n\n const localEnd = Math.min(result.end - lineOffset, text.length);\n\n const hasTextBeforeResult = localStart > localOffset;\n if (hasTextBeforeResult) {\n chunks.push({ text: text.slice(localOffset, localStart), modifiers });\n }\n const hasResultText = localEnd > localStart;\n if (hasResultText) {\n chunks.push({\n modifiers,\n highlight: resultIndex,\n text: text.slice(localStart, localEnd),\n });\n }\n\n localOffset = localEnd;\n\n const foundCompleteResult = result.end - lineOffset === localEnd;\n if (foundCompleteResult) {\n resultIndex += 1;\n result = results[resultIndex];\n } else {\n break; // The rest of the result is in the following chunks\n }\n }\n\n const hasTextAfterResult = localOffset < text.length;\n if (hasTextAfterResult) {\n chunks.push({ text: text.slice(localOffset), modifiers });\n }\n\n lineOffset += text.length;\n }\n\n return chunks;\n}\n\nconst renderLink = ({\n attributes,\n content,\n}: {\n attributes: { [attr: string]: any };\n content: string;\n}) => {\n const { href, ...props } = attributes;\n return (\n <Link to={href} {...props}>\n {content}\n </Link>\n );\n};\n\nexport interface LogLineProps {\n line: AnsiLine;\n classes: ReturnType<typeof useStyles>;\n searchText: string;\n highlightResultIndex?: number;\n}\n\nexport function LogLine({\n line,\n classes,\n searchText,\n highlightResultIndex,\n}: LogLineProps) {\n const chunks = useMemo(\n () => calculateHighlightedChunks(line, searchText),\n [line, searchText],\n );\n\n const elements = useMemo(\n () =>\n chunks.map(({ text, modifiers, highlight }, index) => (\n // eslint-disable-next-line react/forbid-elements\n <span\n key={index}\n className={classnames(\n getModifierClasses(classes, modifiers),\n highlight !== undefined &&\n (highlight === highlightResultIndex\n ? classes.textSelectedHighlight\n : classes.textHighlight),\n )}\n >\n <Linkify options={{ render: renderLink }}>{text}</Linkify>\n </span>\n )),\n [chunks, highlightResultIndex, classes],\n );\n\n return <>{elements}</>;\n}\n"],"names":["classnames"],"mappings":"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"}
1
+ {"version":3,"file":"LogLine.esm.js","sources":["../../../src/components/LogViewer/LogLine.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 { useEffect, useMemo, useRef } from 'react';\nimport { AnsiChunk, AnsiLine, ChunkModifiers } from './AnsiProcessor';\nimport startCase from 'lodash/startCase';\nimport classnames from 'classnames';\nimport { useStyles } from './styles';\nimport Linkify from 'linkify-react';\nimport { Link } from '../Link';\n\nexport function getModifierClasses(\n classes: ReturnType<typeof useStyles>,\n modifiers: ChunkModifiers,\n) {\n const classNames = new Array<string>();\n if (modifiers.bold) {\n classNames.push(classes.modifierBold);\n }\n if (modifiers.italic) {\n classNames.push(classes.modifierItalic);\n }\n if (modifiers.underline) {\n classNames.push(classes.modifierUnderline);\n }\n if (modifiers.foreground) {\n const key = `modifierForeground${startCase(\n modifiers.foreground,\n )}` as keyof typeof classes;\n classNames.push(classes[key]);\n }\n if (modifiers.background) {\n const key = `modifierBackground${startCase(\n modifiers.background,\n )}` as keyof typeof classes;\n classNames.push(classes[key]);\n }\n return classNames.length > 0 ? classNames.join(' ') : undefined;\n}\n\nexport function findSearchResults(text: string, searchText: string) {\n if (!searchText || !text.includes(searchText)) {\n return undefined;\n }\n const searchResults = new Array<{ start: number; end: number }>();\n let offset = 0;\n for (;;) {\n const start = text.indexOf(searchText, offset);\n if (start === -1) {\n break;\n }\n const end = start + searchText.length;\n searchResults.push({ start, end });\n offset = end;\n }\n return searchResults;\n}\n\nexport interface HighlightAnsiChunk extends AnsiChunk {\n highlight?: number;\n}\n\nexport function calculateHighlightedChunks(\n line: AnsiLine,\n searchText: string,\n): HighlightAnsiChunk[] {\n const results = findSearchResults(line.text, searchText);\n if (!results) {\n return line.chunks;\n }\n\n const chunks = new Array<HighlightAnsiChunk>();\n\n let lineOffset = 0;\n let resultIndex = 0;\n let result = results[resultIndex];\n for (const chunk of line.chunks) {\n const { text, modifiers } = chunk;\n if (!result || lineOffset + text.length < result.start) {\n chunks.push(chunk);\n lineOffset += text.length;\n continue;\n }\n\n let localOffset = 0;\n while (result) {\n const localStart = Math.max(result.start - lineOffset, 0);\n if (localStart > text.length) {\n break; // The next result is not in this chunk\n }\n\n const localEnd = Math.min(result.end - lineOffset, text.length);\n\n const hasTextBeforeResult = localStart > localOffset;\n if (hasTextBeforeResult) {\n chunks.push({ text: text.slice(localOffset, localStart), modifiers });\n }\n const hasResultText = localEnd > localStart;\n if (hasResultText) {\n chunks.push({\n modifiers,\n highlight: resultIndex,\n text: text.slice(localStart, localEnd),\n });\n }\n\n localOffset = localEnd;\n\n const foundCompleteResult = result.end - lineOffset === localEnd;\n if (foundCompleteResult) {\n resultIndex += 1;\n result = results[resultIndex];\n } else {\n break; // The rest of the result is in the following chunks\n }\n }\n\n const hasTextAfterResult = localOffset < text.length;\n if (hasTextAfterResult) {\n chunks.push({ text: text.slice(localOffset), modifiers });\n }\n\n lineOffset += text.length;\n }\n\n return chunks;\n}\n\nconst renderLink = ({\n attributes,\n content,\n}: {\n attributes: { [attr: string]: any };\n content: string;\n}) => {\n const { href, ...props } = attributes;\n return (\n <Link to={href} {...props}>\n {content}\n </Link>\n );\n};\n\nexport interface LogLineProps {\n line: AnsiLine;\n classes: ReturnType<typeof useStyles>;\n searchText: string;\n highlightResultIndex?: number;\n setRowHeight?: (index: number, size: number) => void;\n}\n\nexport function LogLine({\n line,\n classes,\n searchText,\n highlightResultIndex,\n setRowHeight,\n}: LogLineProps) {\n const lineRef = useRef<HTMLSpanElement>(null);\n const chunks = useMemo(\n () => calculateHighlightedChunks(line, searchText),\n [line, searchText],\n );\n\n useEffect(() => {\n if (lineRef.current && setRowHeight) {\n setRowHeight(line.lineNumber, lineRef.current.offsetHeight);\n }\n }, [line.lineNumber, setRowHeight]);\n\n const elements = useMemo(\n () =>\n chunks.map(({ text, modifiers, highlight }, index) => (\n // eslint-disable-next-line react/forbid-elements\n <span\n key={index}\n className={classnames(\n getModifierClasses(classes, modifiers),\n highlight !== undefined &&\n (highlight === highlightResultIndex\n ? classes.textSelectedHighlight\n : classes.textHighlight),\n { [classes.textWrap]: !!setRowHeight },\n )}\n >\n <Linkify options={{ render: renderLink }}>{text}</Linkify>\n </span>\n )),\n [chunks, highlightResultIndex, classes, setRowHeight],\n );\n\n return <span ref={lineRef}>{elements}</span>;\n}\n"],"names":["classnames"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"LogViewer.esm.js","sources":["../../../src/components/LogViewer/LogViewer.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 { lazy, Suspense } from 'react';\nimport { useApp } from '@backstage/core-plugin-api';\n\nconst RealLogViewer = lazy(() =>\n import('./RealLogViewer').then(m => ({ default: m.RealLogViewer })),\n);\n\n/**\n * The properties for the LogViewer component.\n *\n * @public\n */\nexport interface LogViewerProps {\n /**\n * The text of the logs to display.\n *\n * The LogViewer component is optimized for appending content at the end of the text.\n */\n text: string;\n /**\n * Styling overrides for classes within the LogViewer component.\n */\n classes?: {\n root?: string;\n };\n}\n\n/**\n * A component that displays logs in a scrollable text area.\n *\n * @remarks\n * The LogViewer has support for search and filtering, as well as displaying\n * text content with ANSI color escape codes.\n *\n * Since the LogViewer uses windowing to avoid rendering all contents at once, the\n * log is sized automatically to fill the available vertical space. This means\n * it may often be needed to wrap the LogViewer in a container that provides it\n * with a fixed amount of space.\n *\n * @public\n */\nexport function LogViewer(props: LogViewerProps) {\n const { Progress } = useApp().getComponents();\n return (\n <Suspense fallback={<Progress />}>\n <RealLogViewer {...props} />\n </Suspense>\n );\n}\n"],"names":[],"mappings":";;;;AAmBA,MAAM,aAAgB,GAAA,IAAA;AAAA,EAAK,MACzB,OAAO,wBAAiB,CAAE,CAAA,IAAA,CAAK,QAAM,EAAE,OAAA,EAAS,CAAE,CAAA,aAAA,EAAgB,CAAA;AACpE,CAAA;AAoCO,SAAS,UAAU,KAAuB,EAAA;AAC/C,EAAA,MAAM,EAAE,QAAA,EAAa,GAAA,MAAA,GAAS,aAAc,EAAA;AAC5C,EACE,uBAAA,GAAA,CAAC,QAAS,EAAA,EAAA,QAAA,kBAAW,GAAA,CAAA,QAAA,EAAA,EAAS,GAC5B,QAAC,kBAAA,GAAA,CAAA,aAAA,EAAA,EAAe,GAAG,KAAA,EAAO,CAC5B,EAAA,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"LogViewer.esm.js","sources":["../../../src/components/LogViewer/LogViewer.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 { lazy, Suspense } from 'react';\nimport { useApp } from '@backstage/core-plugin-api';\n\nconst RealLogViewer = lazy(() =>\n import('./RealLogViewer').then(m => ({ default: m.RealLogViewer })),\n);\n\n/**\n * The properties for the LogViewer component.\n *\n * @public\n */\nexport interface LogViewerProps {\n /**\n * The text of the logs to display.\n *\n * The LogViewer component is optimized for appending content at the end of the text.\n */\n text: string;\n /**\n * Determines if the overflow text should be wrapped or shown via a single line in a horizontal scrollbar.\n */\n textWrap?: boolean;\n /**\n * Styling overrides for classes within the LogViewer component.\n */\n classes?: {\n root?: string;\n };\n}\n\n/**\n * A component that displays logs in a scrollable text area.\n *\n * @remarks\n * The LogViewer has support for search and filtering, as well as displaying\n * text content with ANSI color escape codes.\n *\n * Since the LogViewer uses windowing to avoid rendering all contents at once, the\n * log is sized automatically to fill the available vertical space. This means\n * it may often be needed to wrap the LogViewer in a container that provides it\n * with a fixed amount of space.\n *\n * @public\n */\nexport function LogViewer(props: LogViewerProps) {\n const { Progress } = useApp().getComponents();\n return (\n <Suspense fallback={<Progress />}>\n <RealLogViewer {...props} />\n </Suspense>\n );\n}\n"],"names":[],"mappings":";;;;AAmBA,MAAM,aAAgB,GAAA,IAAA;AAAA,EAAK,MACzB,OAAO,wBAAiB,CAAE,CAAA,IAAA,CAAK,QAAM,EAAE,OAAA,EAAS,CAAE,CAAA,aAAA,EAAgB,CAAA;AACpE,CAAA;AAwCO,SAAS,UAAU,KAAuB,EAAA;AAC/C,EAAA,MAAM,EAAE,QAAA,EAAa,GAAA,MAAA,GAAS,aAAc,EAAA;AAC5C,EACE,uBAAA,GAAA,CAAC,QAAS,EAAA,EAAA,QAAA,kBAAW,GAAA,CAAA,QAAA,EAAA,EAAS,GAC5B,QAAC,kBAAA,GAAA,CAAA,aAAA,EAAA,EAAe,GAAG,KAAA,EAAO,CAC5B,EAAA,CAAA;AAEJ;;;;"}
@@ -3,10 +3,10 @@ import Box from '@material-ui/core/Box';
3
3
  import IconButton from '@material-ui/core/IconButton';
4
4
  import CopyIcon from '@material-ui/icons/FileCopy';
5
5
  import classNames from 'classnames';
6
- import { useState, useMemo, useEffect } from 'react';
6
+ import { useState, useRef, useMemo, useEffect } from 'react';
7
7
  import { useLocation } from 'react-router-dom';
8
8
  import AutoSizer from 'react-virtualized-auto-sizer';
9
- import { FixedSizeList } from 'react-window';
9
+ import { VariableSizeList, FixedSizeList } from 'react-window';
10
10
  import { AnsiProcessor } from './AnsiProcessor.esm.js';
11
11
  import { LogLine } from './LogLine.esm.js';
12
12
  import { LogViewerControls } from './LogViewerControls.esm.js';
@@ -16,27 +16,29 @@ import { useLogViewerSelection } from './useLogViewerSelection.esm.js';
16
16
 
17
17
  function RealLogViewer(props) {
18
18
  const classes = useStyles({ classes: props.classes });
19
- const [fixedListInstance, setFixedListInstance] = useState(null);
19
+ const [listInstance, setListInstance] = useState(null);
20
+ const shouldTextWrap = props.textWrap ?? false;
21
+ const heights = useRef({});
20
22
  const processor = useMemo(() => new AnsiProcessor(), []);
21
23
  const lines = processor.process(props.text);
22
24
  const search = useLogViewerSearch(lines);
23
25
  const selection = useLogViewerSelection(lines);
24
26
  const location = useLocation();
25
27
  useEffect(() => {
26
- if (fixedListInstance) {
27
- fixedListInstance.scrollToItem(lines.length - 1, "end");
28
+ if (listInstance) {
29
+ listInstance.scrollToItem(lines.length - 1, "end");
28
30
  }
29
- }, [fixedListInstance, lines]);
31
+ }, [listInstance, lines]);
30
32
  useEffect(() => {
31
- if (!fixedListInstance) {
33
+ if (!listInstance) {
32
34
  return;
33
35
  }
34
36
  if (search.resultLine) {
35
- fixedListInstance.scrollToItem(search.resultLine - 1, "center");
37
+ listInstance.scrollToItem(search.resultLine - 1, "center");
36
38
  } else {
37
- fixedListInstance.scrollToItem(lines.length - 1, "end");
39
+ listInstance.scrollToItem(lines.length - 1, "end");
38
40
  }
39
- }, [fixedListInstance, search.resultLine, lines]);
41
+ }, [listInstance, search.resultLine, lines]);
40
42
  useEffect(() => {
41
43
  if (location.hash) {
42
44
  const line = parseInt(location.hash.replace(/\D/g, ""), 10);
@@ -46,69 +48,87 @@ function RealLogViewer(props) {
46
48
  const handleSelectLine = (line, event) => {
47
49
  selection.setSelection(line, event.shiftKey);
48
50
  };
49
- return /* @__PURE__ */ jsx(AutoSizer, { children: ({ height, width }) => /* @__PURE__ */ jsxs(Box, { style: { width, height }, className: classes.root, children: [
50
- /* @__PURE__ */ jsx(Box, { className: classes.header, children: /* @__PURE__ */ jsx(LogViewerControls, { ...search }) }),
51
- /* @__PURE__ */ jsx(
52
- FixedSizeList,
53
- {
54
- ref: (instance) => {
55
- setFixedListInstance(instance);
56
- },
57
- className: classes.log,
58
- height: (height || 480) - HEADER_SIZE,
59
- width: width || 640,
60
- itemData: search.lines,
61
- itemSize: 20,
62
- itemCount: search.lines.length,
63
- children: ({ index, style, data }) => {
64
- const line = data[index];
65
- const { lineNumber } = line;
66
- return /* @__PURE__ */ jsxs(
67
- Box,
68
- {
69
- style: { ...style },
70
- className: classNames(classes.line, {
71
- [classes.lineSelected]: selection.isSelected(lineNumber)
72
- }),
73
- children: [
74
- selection.shouldShowButton(lineNumber) && /* @__PURE__ */ jsx(
75
- IconButton,
76
- {
77
- "data-testid": "copy-button",
78
- size: "small",
79
- className: classes.lineCopyButton,
80
- onClick: () => selection.copySelection(),
81
- children: /* @__PURE__ */ jsx(CopyIcon, { fontSize: "inherit" })
82
- }
83
- ),
84
- /* @__PURE__ */ jsx(
85
- "a",
86
- {
87
- role: "row",
88
- target: "_self",
89
- href: `#line-${lineNumber}`,
90
- className: classes.lineNumber,
91
- onClick: (event) => handleSelectLine(lineNumber, event),
92
- onKeyPress: (event) => handleSelectLine(lineNumber, event),
93
- children: lineNumber
94
- }
95
- ),
96
- /* @__PURE__ */ jsx(
97
- LogLine,
98
- {
99
- line,
100
- classes,
101
- searchText: search.searchText,
102
- highlightResultIndex: search.resultLine === lineNumber ? search.resultLineIndex : void 0
103
- }
104
- )
105
- ]
106
- }
107
- );
51
+ function setRowHeight(index, size) {
52
+ if (shouldTextWrap && listInstance) {
53
+ listInstance.resetAfterIndex(0);
54
+ heights.current[index - 1] = size;
55
+ }
56
+ }
57
+ function getRowHeight(index) {
58
+ return heights.current[index] || 20;
59
+ }
60
+ return /* @__PURE__ */ jsx(AutoSizer, { children: ({ height, width }) => {
61
+ const commonProps = {
62
+ ref: setListInstance,
63
+ className: classes.log,
64
+ height: (height || 480) - HEADER_SIZE,
65
+ width: width || 640,
66
+ itemData: search.lines,
67
+ itemCount: search.lines.length
68
+ };
69
+ const renderItem = ({
70
+ index,
71
+ style,
72
+ data
73
+ }) => {
74
+ const line = data[index];
75
+ const { lineNumber } = line;
76
+ return /* @__PURE__ */ jsxs(
77
+ Box,
78
+ {
79
+ style: { ...style },
80
+ className: classNames(classes.line, {
81
+ [classes.lineSelected]: selection.isSelected(lineNumber)
82
+ }),
83
+ children: [
84
+ selection.shouldShowButton(lineNumber) && /* @__PURE__ */ jsx(
85
+ IconButton,
86
+ {
87
+ "data-testid": "copy-button",
88
+ size: "small",
89
+ className: classes.lineCopyButton,
90
+ onClick: () => selection.copySelection(),
91
+ children: /* @__PURE__ */ jsx(CopyIcon, { fontSize: "inherit" })
92
+ }
93
+ ),
94
+ /* @__PURE__ */ jsx(
95
+ "a",
96
+ {
97
+ role: "row",
98
+ target: "_self",
99
+ href: `#line-${lineNumber}`,
100
+ className: classes.lineNumber,
101
+ onClick: (event) => handleSelectLine(lineNumber, event),
102
+ onKeyPress: (event) => handleSelectLine(lineNumber, event),
103
+ children: lineNumber
104
+ }
105
+ ),
106
+ /* @__PURE__ */ jsx(
107
+ LogLine,
108
+ {
109
+ setRowHeight: shouldTextWrap ? setRowHeight : void 0,
110
+ line,
111
+ classes,
112
+ searchText: search.searchText,
113
+ highlightResultIndex: search.resultLine === lineNumber ? search.resultLineIndex : void 0
114
+ }
115
+ )
116
+ ]
117
+ }
118
+ );
119
+ };
120
+ return /* @__PURE__ */ jsxs(Box, { style: { width, height }, className: classes.root, children: [
121
+ /* @__PURE__ */ jsx(Box, { className: classes.header, children: /* @__PURE__ */ jsx(LogViewerControls, { ...search }) }),
122
+ shouldTextWrap ? /* @__PURE__ */ jsx(
123
+ VariableSizeList,
124
+ {
125
+ ...commonProps,
126
+ itemSize: getRowHeight,
127
+ children: renderItem
108
128
  }
109
- }
110
- )
111
- ] }) });
129
+ ) : /* @__PURE__ */ jsx(FixedSizeList, { ...commonProps, itemSize: 20, children: renderItem })
130
+ ] });
131
+ } });
112
132
  }
113
133
 
114
134
  export { RealLogViewer };
@@ -1 +1 @@
1
- {"version":3,"file":"RealLogViewer.esm.js","sources":["../../../src/components/LogViewer/RealLogViewer.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 IconButton from '@material-ui/core/IconButton';\nimport CopyIcon from '@material-ui/icons/FileCopy';\nimport classnames from 'classnames';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport AutoSizer from 'react-virtualized-auto-sizer';\nimport { FixedSizeList } from 'react-window';\n\nimport { AnsiLine, AnsiProcessor } from './AnsiProcessor';\nimport { LogLine } from './LogLine';\nimport { LogViewerControls } from './LogViewerControls';\nimport { HEADER_SIZE, useStyles } from './styles';\nimport { useLogViewerSearch } from './useLogViewerSearch';\nimport { useLogViewerSelection } from './useLogViewerSelection';\n\nexport interface RealLogViewerProps {\n text: string;\n classes?: { root?: string };\n}\n\nexport function RealLogViewer(props: RealLogViewerProps) {\n const classes = useStyles({ classes: props.classes });\n const [fixedListInstance, setFixedListInstance] = useState<FixedSizeList<\n AnsiLine[]\n > | null>(null);\n\n // The processor keeps state that optimizes appending to the text\n const processor = useMemo(() => new AnsiProcessor(), []);\n const lines = processor.process(props.text);\n\n const search = useLogViewerSearch(lines);\n const selection = useLogViewerSelection(lines);\n const location = useLocation();\n\n useEffect(() => {\n if (fixedListInstance) {\n fixedListInstance.scrollToItem(lines.length - 1, 'end');\n }\n }, [fixedListInstance, lines]);\n\n useEffect(() => {\n if (!fixedListInstance) {\n return;\n }\n if (search.resultLine) {\n fixedListInstance.scrollToItem(search.resultLine - 1, 'center');\n } else {\n fixedListInstance.scrollToItem(lines.length - 1, 'end');\n }\n }, [fixedListInstance, search.resultLine, lines]);\n\n useEffect(() => {\n if (location.hash) {\n // #line-6 -> 6\n const line = parseInt(location.hash.replace(/\\D/g, ''), 10);\n selection.setSelection(line, false);\n }\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleSelectLine = (\n line: number,\n event: { shiftKey: boolean; preventDefault: () => void },\n ) => {\n selection.setSelection(line, event.shiftKey);\n };\n\n return (\n <AutoSizer>\n {({ height, width }: { height?: number; width?: number }) => (\n <Box style={{ width, height }} className={classes.root}>\n <Box className={classes.header}>\n <LogViewerControls {...search} />\n </Box>\n <FixedSizeList\n ref={(instance: FixedSizeList<AnsiLine[]>) => {\n setFixedListInstance(instance);\n }}\n className={classes.log}\n height={(height || 480) - HEADER_SIZE}\n width={width || 640}\n itemData={search.lines}\n itemSize={20}\n itemCount={search.lines.length}\n >\n {({ index, style, data }) => {\n const line = data[index];\n const { lineNumber } = line;\n return (\n <Box\n style={{ ...style }}\n className={classnames(classes.line, {\n [classes.lineSelected]: selection.isSelected(lineNumber),\n })}\n >\n {selection.shouldShowButton(lineNumber) && (\n <IconButton\n data-testid=\"copy-button\"\n size=\"small\"\n className={classes.lineCopyButton}\n onClick={() => selection.copySelection()}\n >\n <CopyIcon fontSize=\"inherit\" />\n </IconButton>\n )}\n <a\n role=\"row\"\n target=\"_self\"\n href={`#line-${lineNumber}`}\n className={classes.lineNumber}\n onClick={event => handleSelectLine(lineNumber, event)}\n onKeyPress={event => handleSelectLine(lineNumber, event)}\n >\n {lineNumber}\n </a>\n <LogLine\n line={line}\n classes={classes}\n searchText={search.searchText}\n highlightResultIndex={\n search.resultLine === lineNumber\n ? search.resultLineIndex\n : undefined\n }\n />\n </Box>\n );\n }}\n </FixedSizeList>\n </Box>\n )}\n </AutoSizer>\n );\n}\n"],"names":["classnames"],"mappings":"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"}
1
+ {"version":3,"file":"RealLogViewer.esm.js","sources":["../../../src/components/LogViewer/RealLogViewer.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 IconButton from '@material-ui/core/IconButton';\nimport CopyIcon from '@material-ui/icons/FileCopy';\nimport classnames from 'classnames';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport AutoSizer from 'react-virtualized-auto-sizer';\nimport { VariableSizeList, FixedSizeList } from 'react-window';\n\nimport { AnsiLine, AnsiProcessor } from './AnsiProcessor';\nimport { LogLine } from './LogLine';\nimport { LogViewerControls } from './LogViewerControls';\nimport { HEADER_SIZE, useStyles } from './styles';\nimport { useLogViewerSearch } from './useLogViewerSearch';\nimport { useLogViewerSelection } from './useLogViewerSelection';\n\nexport interface RealLogViewerProps {\n text: string;\n textWrap?: boolean;\n classes?: { root?: string };\n}\n\nexport function RealLogViewer(props: RealLogViewerProps) {\n const classes = useStyles({ classes: props.classes });\n const [listInstance, setListInstance] = useState<\n VariableSizeList<AnsiLine[]> | FixedSizeList<AnsiLine[]> | null\n >(null);\n const shouldTextWrap = props.textWrap ?? false;\n const heights = useRef<{ [key: number]: number }>({});\n\n // The processor keeps state that optimizes appending to the text\n const processor = useMemo(() => new AnsiProcessor(), []);\n const lines = processor.process(props.text);\n\n const search = useLogViewerSearch(lines);\n const selection = useLogViewerSelection(lines);\n const location = useLocation();\n\n useEffect(() => {\n if (listInstance) {\n listInstance.scrollToItem(lines.length - 1, 'end');\n }\n }, [listInstance, lines]);\n\n useEffect(() => {\n if (!listInstance) {\n return;\n }\n if (search.resultLine) {\n listInstance.scrollToItem(search.resultLine - 1, 'center');\n } else {\n listInstance.scrollToItem(lines.length - 1, 'end');\n }\n }, [listInstance, search.resultLine, lines]);\n\n useEffect(() => {\n if (location.hash) {\n // #line-6 -> 6\n const line = parseInt(location.hash.replace(/\\D/g, ''), 10);\n selection.setSelection(line, false);\n }\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleSelectLine = (\n line: number,\n event: { shiftKey: boolean; preventDefault: () => void },\n ) => {\n selection.setSelection(line, event.shiftKey);\n };\n\n function setRowHeight(index: number, size: number) {\n if (shouldTextWrap && listInstance) {\n (listInstance as VariableSizeList<AnsiLine[]>).resetAfterIndex(0);\n // lineNumber is 1-based but index is 0-based\n heights.current[index - 1] = size;\n }\n }\n\n function getRowHeight(index: number) {\n return heights.current[index] || 20;\n }\n\n return (\n <AutoSizer>\n {({ height, width }: { height?: number; width?: number }) => {\n const commonProps = {\n ref: setListInstance,\n className: classes.log,\n height: (height || 480) - HEADER_SIZE,\n width: width || 640,\n itemData: search.lines,\n itemCount: search.lines.length,\n };\n\n const renderItem = ({\n index,\n style,\n data,\n }: {\n index: number;\n style: React.CSSProperties;\n data: AnsiLine[];\n }) => {\n const line = data[index];\n const { lineNumber } = line;\n return (\n <Box\n style={{ ...style }}\n className={classnames(classes.line, {\n [classes.lineSelected]: selection.isSelected(lineNumber),\n })}\n >\n {selection.shouldShowButton(lineNumber) && (\n <IconButton\n data-testid=\"copy-button\"\n size=\"small\"\n className={classes.lineCopyButton}\n onClick={() => selection.copySelection()}\n >\n <CopyIcon fontSize=\"inherit\" />\n </IconButton>\n )}\n <a\n role=\"row\"\n target=\"_self\"\n href={`#line-${lineNumber}`}\n className={classes.lineNumber}\n onClick={event => handleSelectLine(lineNumber, event)}\n onKeyPress={event => handleSelectLine(lineNumber, event)}\n >\n {lineNumber}\n </a>\n <LogLine\n setRowHeight={shouldTextWrap ? setRowHeight : undefined}\n line={line}\n classes={classes}\n searchText={search.searchText}\n highlightResultIndex={\n search.resultLine === lineNumber\n ? search.resultLineIndex\n : undefined\n }\n />\n </Box>\n );\n };\n\n return (\n <Box style={{ width, height }} className={classes.root}>\n <Box className={classes.header}>\n <LogViewerControls {...search} />\n </Box>\n {shouldTextWrap ? (\n <VariableSizeList<AnsiLine[]>\n {...commonProps}\n itemSize={getRowHeight}\n >\n {renderItem}\n </VariableSizeList>\n ) : (\n <FixedSizeList<AnsiLine[]> {...commonProps} itemSize={20}>\n {renderItem}\n </FixedSizeList>\n )}\n </Box>\n );\n }}\n </AutoSizer>\n );\n}\n"],"names":["classnames"],"mappings":"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"}
@@ -15,11 +15,14 @@ const useStyles = makeStyles(
15
15
  },
16
16
  log: {
17
17
  fontFamily: '"Monaco", monospace',
18
- fontSize: theme.typography.pxToRem(12)
18
+ fontSize: theme.typography.pxToRem(12),
19
+ lineHeight: "20px"
19
20
  },
20
21
  line: {
21
22
  position: "relative",
22
23
  whiteSpace: "pre",
24
+ display: "flex",
25
+ alignItems: "flex-start",
23
26
  "&:hover": {
24
27
  background: theme.palette.action.hover
25
28
  }
@@ -40,7 +43,8 @@ const useStyles = makeStyles(
40
43
  textAlign: "end",
41
44
  width: 60,
42
45
  marginRight: theme.spacing(1),
43
- cursor: "pointer"
46
+ cursor: "pointer",
47
+ flexShrink: 0
44
48
  },
45
49
  textHighlight: {
46
50
  background: alpha(theme.palette.info.main, 0.15)
@@ -110,6 +114,10 @@ const useStyles = makeStyles(
110
114
  },
111
115
  modifierBackgroundGrey: {
112
116
  background: colors.grey[500]
117
+ },
118
+ textWrap: {
119
+ whiteSpace: "pre-wrap",
120
+ wordBreak: "break-all"
113
121
  }
114
122
  }),
115
123
  { name: "BackstageLogViewer" }
@@ -1 +1 @@
1
- {"version":3,"file":"styles.esm.js","sources":["../../../src/components/LogViewer/styles.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { alpha, makeStyles } from '@material-ui/core/styles';\nimport * as colors from '@material-ui/core/colors';\n\nexport const HEADER_SIZE = 40;\n\n/** @public Class keys for overriding LogViewer styles */\nexport type LogViewerClassKey =\n | 'root'\n | 'header'\n | 'log'\n | 'line'\n | 'lineSelected'\n | 'lineCopyButton'\n | 'lineNumber'\n | 'textHighlight'\n | 'textSelectedHighlight'\n | 'modifierBold'\n | 'modifierItalic'\n | 'modifierUnderline'\n | 'modifierForegroundBlack'\n | 'modifierForegroundRed'\n | 'modifierForegroundGreen'\n | 'modifierForegroundYellow'\n | 'modifierForegroundBlue'\n | 'modifierForegroundMagenta'\n | 'modifierForegroundCyan'\n | 'modifierForegroundWhite'\n | 'modifierForegroundGrey'\n | 'modifierBackgroundBlack'\n | 'modifierBackgroundRed'\n | 'modifierBackgroundGreen'\n | 'modifierBackgroundYellow'\n | 'modifierBackgroundBlue'\n | 'modifierBackgroundMagenta'\n | 'modifierBackgroundCyan'\n | 'modifierBackgroundWhite'\n | 'modifierBackgroundGrey';\n\nexport const useStyles = makeStyles(\n theme => ({\n root: {\n background: theme.palette.background.paper,\n },\n header: {\n height: HEADER_SIZE,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n },\n log: {\n fontFamily: '\"Monaco\", monospace',\n fontSize: theme.typography.pxToRem(12),\n },\n line: {\n position: 'relative',\n whiteSpace: 'pre',\n\n '&:hover': {\n background: theme.palette.action.hover,\n },\n },\n lineSelected: {\n background: theme.palette.action.selected,\n\n '&:hover': {\n background: theme.palette.action.selected,\n },\n },\n lineCopyButton: {\n position: 'absolute',\n paddingTop: 0,\n paddingBottom: 0,\n },\n lineNumber: {\n display: 'inline-block',\n textAlign: 'end',\n width: 60,\n marginRight: theme.spacing(1),\n cursor: 'pointer',\n },\n textHighlight: {\n background: alpha(theme.palette.info.main, 0.15),\n },\n textSelectedHighlight: {\n background: alpha(theme.palette.info.main, 0.4),\n },\n modifierBold: {\n fontWeight: theme.typography.fontWeightBold,\n },\n modifierItalic: {\n fontStyle: 'italic',\n },\n modifierUnderline: {\n textDecoration: 'underline',\n },\n modifierForegroundBlack: {\n color: colors.common.black,\n },\n modifierForegroundRed: {\n color: colors.red[500],\n },\n modifierForegroundGreen: {\n color: colors.green[500],\n },\n modifierForegroundYellow: {\n color: colors.yellow[500],\n },\n modifierForegroundBlue: {\n color: colors.blue[500],\n },\n modifierForegroundMagenta: {\n color: colors.purple[500],\n },\n modifierForegroundCyan: {\n color: colors.cyan[500],\n },\n modifierForegroundWhite: {\n color: colors.common.white,\n },\n modifierForegroundGrey: {\n color: colors.grey[500],\n },\n modifierBackgroundBlack: {\n background: colors.common.black,\n },\n modifierBackgroundRed: {\n background: colors.red[500],\n },\n modifierBackgroundGreen: {\n background: colors.green[500],\n },\n modifierBackgroundYellow: {\n background: colors.yellow[500],\n },\n modifierBackgroundBlue: {\n background: colors.blue[500],\n },\n modifierBackgroundMagenta: {\n background: colors.purple[500],\n },\n modifierBackgroundCyan: {\n background: colors.cyan[500],\n },\n modifierBackgroundWhite: {\n background: colors.common.white,\n },\n modifierBackgroundGrey: {\n background: colors.grey[500],\n },\n }),\n { name: 'BackstageLogViewer' },\n);\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"styles.esm.js","sources":["../../../src/components/LogViewer/styles.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { alpha, makeStyles } from '@material-ui/core/styles';\nimport * as colors from '@material-ui/core/colors';\n\nexport const HEADER_SIZE = 40;\n\n/** @public Class keys for overriding LogViewer styles */\nexport type LogViewerClassKey =\n | 'root'\n | 'header'\n | 'log'\n | 'line'\n | 'lineSelected'\n | 'lineCopyButton'\n | 'lineNumber'\n | 'textHighlight'\n | 'textSelectedHighlight'\n | 'modifierBold'\n | 'modifierItalic'\n | 'modifierUnderline'\n | 'modifierForegroundBlack'\n | 'modifierForegroundRed'\n | 'modifierForegroundGreen'\n | 'modifierForegroundYellow'\n | 'modifierForegroundBlue'\n | 'modifierForegroundMagenta'\n | 'modifierForegroundCyan'\n | 'modifierForegroundWhite'\n | 'modifierForegroundGrey'\n | 'modifierBackgroundBlack'\n | 'modifierBackgroundRed'\n | 'modifierBackgroundGreen'\n | 'modifierBackgroundYellow'\n | 'modifierBackgroundBlue'\n | 'modifierBackgroundMagenta'\n | 'modifierBackgroundCyan'\n | 'modifierBackgroundWhite'\n | 'modifierBackgroundGrey';\n\nexport const useStyles = makeStyles(\n theme => ({\n root: {\n background: theme.palette.background.paper,\n },\n header: {\n height: HEADER_SIZE,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n },\n log: {\n fontFamily: '\"Monaco\", monospace',\n fontSize: theme.typography.pxToRem(12),\n lineHeight: '20px',\n },\n line: {\n position: 'relative',\n whiteSpace: 'pre',\n display: 'flex',\n alignItems: 'flex-start',\n\n '&:hover': {\n background: theme.palette.action.hover,\n },\n },\n lineSelected: {\n background: theme.palette.action.selected,\n\n '&:hover': {\n background: theme.palette.action.selected,\n },\n },\n lineCopyButton: {\n position: 'absolute',\n paddingTop: 0,\n paddingBottom: 0,\n },\n lineNumber: {\n display: 'inline-block',\n textAlign: 'end',\n width: 60,\n marginRight: theme.spacing(1),\n cursor: 'pointer',\n flexShrink: 0,\n },\n textHighlight: {\n background: alpha(theme.palette.info.main, 0.15),\n },\n textSelectedHighlight: {\n background: alpha(theme.palette.info.main, 0.4),\n },\n modifierBold: {\n fontWeight: theme.typography.fontWeightBold,\n },\n modifierItalic: {\n fontStyle: 'italic',\n },\n modifierUnderline: {\n textDecoration: 'underline',\n },\n modifierForegroundBlack: {\n color: colors.common.black,\n },\n modifierForegroundRed: {\n color: colors.red[500],\n },\n modifierForegroundGreen: {\n color: colors.green[500],\n },\n modifierForegroundYellow: {\n color: colors.yellow[500],\n },\n modifierForegroundBlue: {\n color: colors.blue[500],\n },\n modifierForegroundMagenta: {\n color: colors.purple[500],\n },\n modifierForegroundCyan: {\n color: colors.cyan[500],\n },\n modifierForegroundWhite: {\n color: colors.common.white,\n },\n modifierForegroundGrey: {\n color: colors.grey[500],\n },\n modifierBackgroundBlack: {\n background: colors.common.black,\n },\n modifierBackgroundRed: {\n background: colors.red[500],\n },\n modifierBackgroundGreen: {\n background: colors.green[500],\n },\n modifierBackgroundYellow: {\n background: colors.yellow[500],\n },\n modifierBackgroundBlue: {\n background: colors.blue[500],\n },\n modifierBackgroundMagenta: {\n background: colors.purple[500],\n },\n modifierBackgroundCyan: {\n background: colors.cyan[500],\n },\n modifierBackgroundWhite: {\n background: colors.common.white,\n },\n modifierBackgroundGrey: {\n background: colors.grey[500],\n },\n textWrap: {\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-all',\n },\n }),\n { name: 'BackstageLogViewer' },\n);\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useQueryParamState.esm.js","sources":["../../src/hooks/useQueryParamState.ts"],"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 { isEqual } from 'lodash';\nimport qs from 'qs';\nimport { useEffect, useState } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { useDebouncedEffect } from '@react-hookz/web';\n\nfunction stringify(queryParams: any): string {\n // Even though these setting don't look nice (e.g. escaped brackets), we should keep\n // them this way. The current syntax handles all cases, including variable types with\n // arrays or strings.\n return qs.stringify(queryParams, {\n strictNullHandling: true,\n });\n}\n\nfunction parse(queryString: string): any {\n return qs.parse(queryString, {\n ignoreQueryPrefix: true,\n strictNullHandling: true,\n });\n}\n\nfunction extractState(queryString: string, stateName: string): any | undefined {\n const queryParams = parse(queryString);\n\n return queryParams[stateName];\n}\n\nfunction joinQueryString(\n queryString: string,\n stateName: string,\n state: any,\n): string {\n const queryParams = {\n ...parse(queryString),\n [stateName]: state,\n };\n return stringify(queryParams);\n}\n\ntype SetQueryParams<T> = (params: T) => void;\n\nexport function useQueryParamState<T>(\n stateName: string,\n /** @deprecated Don't configure a custom debouceTime */\n debounceTime: number = 250,\n): [T | undefined, SetQueryParams<T>] {\n const [searchParams, setSearchParams] = useSearchParams();\n const searchParamsString = searchParams.toString();\n const [queryParamState, setQueryParamState] = useState<T>(\n extractState(searchParamsString, stateName),\n );\n\n useEffect(() => {\n const newState = extractState(searchParamsString, stateName);\n\n setQueryParamState(oldState =>\n isEqual(newState, oldState) ? oldState : newState,\n );\n }, [searchParamsString, setQueryParamState, stateName]);\n\n useDebouncedEffect(\n () => {\n const queryString = joinQueryString(\n searchParamsString,\n stateName,\n queryParamState,\n );\n\n if (searchParamsString !== queryString) {\n setSearchParams(queryString, { replace: true });\n }\n },\n [setSearchParams, queryParamState, searchParamsString, stateName],\n debounceTime,\n );\n\n return [queryParamState, setQueryParamState];\n}\n"],"names":[],"mappings":";;;;;;AAsBA,SAAS,UAAU,WAA0B,EAAA;AAI3C,EAAO,OAAA,EAAA,CAAG,UAAU,WAAa,EAAA;AAAA,IAC/B,kBAAoB,EAAA;AAAA,GACrB,CAAA;AACH;AAEA,SAAS,MAAM,WAA0B,EAAA;AACvC,EAAO,OAAA,EAAA,CAAG,MAAM,WAAa,EAAA;AAAA,IAC3B,iBAAmB,EAAA,IAAA;AAAA,IACnB,kBAAoB,EAAA;AAAA,GACrB,CAAA;AACH;AAEA,SAAS,YAAA,CAAa,aAAqB,SAAoC,EAAA;AAC7E,EAAM,MAAA,WAAA,GAAc,MAAM,WAAW,CAAA;AAErC,EAAA,OAAO,YAAY,SAAS,CAAA;AAC9B;AAEA,SAAS,eAAA,CACP,WACA,EAAA,SAAA,EACA,KACQ,EAAA;AACR,EAAA,MAAM,WAAc,GAAA;AAAA,IAClB,GAAG,MAAM,WAAW,CAAA;AAAA,IACpB,CAAC,SAAS,GAAG;AAAA,GACf;AACA,EAAA,OAAO,UAAU,WAAW,CAAA;AAC9B;AAIgB,SAAA,kBAAA,CACd,SAEA,EAAA,YAAA,GAAuB,GACa,EAAA;AACpC,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,eAAgB,EAAA;AACxD,EAAM,MAAA,kBAAA,GAAqB,aAAa,QAAS,EAAA;AACjD,EAAM,MAAA,CAAC,eAAiB,EAAA,kBAAkB,CAAI,GAAA,QAAA;AAAA,IAC5C,YAAA,CAAa,oBAAoB,SAAS;AAAA,GAC5C;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAM,MAAA,QAAA,GAAW,YAAa,CAAA,kBAAA,EAAoB,SAAS,CAAA;AAE3D,IAAA,kBAAA;AAAA,MAAmB,CACjB,QAAA,KAAA,OAAA,CAAQ,QAAU,EAAA,QAAQ,IAAI,QAAW,GAAA;AAAA,KAC3C;AAAA,GACC,EAAA,CAAC,kBAAoB,EAAA,kBAAA,EAAoB,SAAS,CAAC,CAAA;AAEtD,EAAA,kBAAA;AAAA,IACE,MAAM;AACJ,MAAA,MAAM,WAAc,GAAA,eAAA;AAAA,QAClB,kBAAA;AAAA,QACA,SAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,IAAI,uBAAuB,WAAa,EAAA;AACtC,QAAA,eAAA,CAAgB,WAAa,EAAA,EAAE,OAAS,EAAA,IAAA,EAAM,CAAA;AAAA;AAChD,KACF;AAAA,IACA,CAAC,eAAA,EAAiB,eAAiB,EAAA,kBAAA,EAAoB,SAAS,CAAA;AAAA,IAChE;AAAA,GACF;AAEA,EAAO,OAAA,CAAC,iBAAiB,kBAAkB,CAAA;AAC7C;;;;"}
1
+ {"version":3,"file":"useQueryParamState.esm.js","sources":["../../src/hooks/useQueryParamState.ts"],"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 { isEqual } from 'lodash';\nimport qs from 'qs';\nimport { useEffect, useState } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { useDebouncedEffect } from '@react-hookz/web';\n\nfunction stringify(queryParams: any): string {\n // Even though these setting don't look nice (e.g. escaped brackets), we should keep\n // them this way. The current syntax handles all cases, including variable types with\n // arrays or strings.\n return qs.stringify(queryParams, {\n strictNullHandling: true,\n });\n}\n\nfunction parse(queryString: string): any {\n return qs.parse(queryString, {\n ignoreQueryPrefix: true,\n strictNullHandling: true,\n });\n}\n\nfunction extractState(queryString: string, stateName: string): any | undefined {\n const queryParams = parse(queryString);\n\n return queryParams[stateName];\n}\n\nfunction joinQueryString(\n queryString: string,\n stateName: string,\n state: any,\n): string {\n const queryParams = {\n ...parse(queryString),\n [stateName]: state,\n };\n return stringify(queryParams);\n}\n\ntype SetQueryParams<T> = (params: T) => void;\n\nexport function useQueryParamState<T>(\n stateName: string,\n /** @deprecated Don't configure a custom debounceTime */\n debounceTime: number = 250,\n): [T | undefined, SetQueryParams<T>] {\n const [searchParams, setSearchParams] = useSearchParams();\n const searchParamsString = searchParams.toString();\n const [queryParamState, setQueryParamState] = useState<T>(\n extractState(searchParamsString, stateName),\n );\n\n useEffect(() => {\n const newState = extractState(searchParamsString, stateName);\n\n setQueryParamState(oldState =>\n isEqual(newState, oldState) ? oldState : newState,\n );\n }, [searchParamsString, setQueryParamState, stateName]);\n\n useDebouncedEffect(\n () => {\n const queryString = joinQueryString(\n searchParamsString,\n stateName,\n queryParamState,\n );\n\n if (searchParamsString !== queryString) {\n setSearchParams(queryString, { replace: true });\n }\n },\n [setSearchParams, queryParamState, searchParamsString, stateName],\n debounceTime,\n );\n\n return [queryParamState, setQueryParamState];\n}\n"],"names":[],"mappings":";;;;;;AAsBA,SAAS,UAAU,WAA0B,EAAA;AAI3C,EAAO,OAAA,EAAA,CAAG,UAAU,WAAa,EAAA;AAAA,IAC/B,kBAAoB,EAAA;AAAA,GACrB,CAAA;AACH;AAEA,SAAS,MAAM,WAA0B,EAAA;AACvC,EAAO,OAAA,EAAA,CAAG,MAAM,WAAa,EAAA;AAAA,IAC3B,iBAAmB,EAAA,IAAA;AAAA,IACnB,kBAAoB,EAAA;AAAA,GACrB,CAAA;AACH;AAEA,SAAS,YAAA,CAAa,aAAqB,SAAoC,EAAA;AAC7E,EAAM,MAAA,WAAA,GAAc,MAAM,WAAW,CAAA;AAErC,EAAA,OAAO,YAAY,SAAS,CAAA;AAC9B;AAEA,SAAS,eAAA,CACP,WACA,EAAA,SAAA,EACA,KACQ,EAAA;AACR,EAAA,MAAM,WAAc,GAAA;AAAA,IAClB,GAAG,MAAM,WAAW,CAAA;AAAA,IACpB,CAAC,SAAS,GAAG;AAAA,GACf;AACA,EAAA,OAAO,UAAU,WAAW,CAAA;AAC9B;AAIgB,SAAA,kBAAA,CACd,SAEA,EAAA,YAAA,GAAuB,GACa,EAAA;AACpC,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,eAAgB,EAAA;AACxD,EAAM,MAAA,kBAAA,GAAqB,aAAa,QAAS,EAAA;AACjD,EAAM,MAAA,CAAC,eAAiB,EAAA,kBAAkB,CAAI,GAAA,QAAA;AAAA,IAC5C,YAAA,CAAa,oBAAoB,SAAS;AAAA,GAC5C;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAM,MAAA,QAAA,GAAW,YAAa,CAAA,kBAAA,EAAoB,SAAS,CAAA;AAE3D,IAAA,kBAAA;AAAA,MAAmB,CACjB,QAAA,KAAA,OAAA,CAAQ,QAAU,EAAA,QAAQ,IAAI,QAAW,GAAA;AAAA,KAC3C;AAAA,GACC,EAAA,CAAC,kBAAoB,EAAA,kBAAA,EAAoB,SAAS,CAAC,CAAA;AAEtD,EAAA,kBAAA;AAAA,IACE,MAAM;AACJ,MAAA,MAAM,WAAc,GAAA,eAAA;AAAA,QAClB,kBAAA;AAAA,QACA,SAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,IAAI,uBAAuB,WAAa,EAAA;AACtC,QAAA,eAAA,CAAgB,WAAa,EAAA,EAAE,OAAS,EAAA,IAAA,EAAM,CAAA;AAAA;AAChD,KACF;AAAA,IACA,CAAC,eAAA,EAAiB,eAAiB,EAAA,kBAAA,EAAoB,SAAS,CAAA;AAAA,IAChE;AAAA,GACF;AAEA,EAAO,OAAA,CAAC,iBAAiB,kBAAkB,CAAA;AAC7C;;;;"}
package/dist/index.d.ts CHANGED
@@ -66,7 +66,7 @@ declare function AlertDisplay(props: AlertDisplayProps): react_jsx_runtime.JSX.E
66
66
  type AutoLogoutProps = {
67
67
  /**
68
68
  * Enable/disable the AutoLogoutMechanism.
69
- * defauls to true.
69
+ * defaults to true.
70
70
  */
71
71
  enabled?: boolean;
72
72
  /**
@@ -802,6 +802,10 @@ interface LogViewerProps {
802
802
  * The LogViewer component is optimized for appending content at the end of the text.
803
803
  */
804
804
  text: string;
805
+ /**
806
+ * Determines if the overflow text should be wrapped or shown via a single line in a horizontal scrollbar.
807
+ */
808
+ textWrap?: boolean;
805
809
  /**
806
810
  * Styling overrides for classes within the LogViewer component.
807
811
  */
@@ -1121,7 +1125,7 @@ declare function StructuredMetadataTable(props: StructuredMetadataTableProps): r
1121
1125
 
1122
1126
  type SetQueryParams<T> = (params: T) => void;
1123
1127
  declare function useQueryParamState<T>(stateName: string,
1124
- /** @deprecated Don't configure a custom debouceTime */
1128
+ /** @deprecated Don't configure a custom debounceTime */
1125
1129
  debounceTime?: number): [T | undefined, SetQueryParams<T>];
1126
1130
 
1127
1131
  type SupportItemLink = {
@@ -15,15 +15,21 @@ import { coreComponentsTranslationRef } from '../../translation.esm.js';
15
15
 
16
16
  const useStyles = makeStyles(
17
17
  (theme) => ({
18
+ root: {
19
+ left: 0,
20
+ top: 0,
21
+ bottom: 0,
22
+ zIndex: theme.zIndex.appBar,
23
+ position: "fixed"
24
+ },
18
25
  drawer: {
19
26
  display: "flex",
20
27
  flexFlow: "column nowrap",
21
28
  alignItems: "flex-start",
22
- position: "fixed",
23
29
  left: 0,
24
30
  top: 0,
25
31
  bottom: 0,
26
- zIndex: theme.zIndex.appBar,
32
+ position: "absolute",
27
33
  background: theme.palette.navigation.background,
28
34
  overflowX: "hidden",
29
35
  msOverflowStyle: "none",
@@ -1 +1 @@
1
- {"version":3,"file":"Bar.esm.js","sources":["../../../src/layout/Sidebar/Bar.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 Button from '@material-ui/core/Button';\nimport { makeStyles, Theme } from '@material-ui/core/styles';\nimport useMediaQuery from '@material-ui/core/useMediaQuery';\nimport classnames from 'classnames';\nimport { ReactNode, useContext, useRef, useState } from 'react';\n\nimport {\n makeSidebarConfig,\n makeSidebarSubmenuConfig,\n SidebarConfig,\n SidebarConfigContext,\n SidebarOptions,\n SubmenuConfig,\n SubmenuOptions,\n} from './config';\nimport { MobileSidebar } from './MobileSidebar';\nimport { useContent } from './Page';\nimport { SidebarOpenStateProvider } from './SidebarOpenStateContext';\nimport { useSidebarPinState } from './SidebarPinStateContext';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { coreComponentsTranslationRef } from '../../translation';\n\n/** @public */\nexport type SidebarClassKey = 'drawer' | 'drawerOpen';\nconst useStyles = makeStyles<Theme, { sidebarConfig: SidebarConfig }>(\n theme => ({\n drawer: {\n display: 'flex',\n flexFlow: 'column nowrap',\n alignItems: 'flex-start',\n position: 'fixed',\n left: 0,\n top: 0,\n bottom: 0,\n zIndex: theme.zIndex.appBar,\n background: theme.palette.navigation.background,\n overflowX: 'hidden',\n msOverflowStyle: 'none',\n scrollbarWidth: 'none',\n transition: theme.transitions.create('width', {\n easing: theme.transitions.easing.sharp,\n duration: theme.transitions.duration.shortest,\n }),\n '& > *': {\n flexShrink: 0,\n },\n '&::-webkit-scrollbar': {\n display: 'none',\n },\n '@media print': {\n display: 'none',\n },\n },\n drawerWidth: props => ({\n width: props.sidebarConfig.drawerWidthClosed,\n }),\n drawerOpen: props => ({\n width: props.sidebarConfig.drawerWidthOpen,\n transition: theme.transitions.create('width', {\n easing: theme.transitions.easing.sharp,\n duration: theme.transitions.duration.shorter,\n }),\n }),\n visuallyHidden: {\n top: 0,\n position: 'absolute',\n zIndex: 1000,\n transform: 'translateY(-200%)',\n '&:focus': {\n transform: 'translateY(5px)',\n },\n },\n }),\n { name: 'BackstageSidebar' },\n);\n\nenum State {\n Closed,\n Idle,\n Open,\n}\n\n/** @public */\nexport type SidebarProps = {\n openDelayMs?: number;\n closeDelayMs?: number;\n sidebarOptions?: SidebarOptions;\n submenuOptions?: SubmenuOptions;\n disableExpandOnHover?: boolean;\n children?: ReactNode;\n};\n\nexport type DesktopSidebarProps = {\n openDelayMs?: number;\n closeDelayMs?: number;\n disableExpandOnHover?: boolean;\n children?: ReactNode;\n};\n\n/**\n * Places the Sidebar & wraps the children providing context weather the `Sidebar` is open or not.\n *\n * Handles & delays hover events for expanding the `Sidebar`\n *\n * @param props `disableExpandOnHover` disables the default hover behaviour;\n * `openDelayMs` & `closeDelayMs` set delay until sidebar will open/close on hover\n * @returns\n * @internal\n */\nconst DesktopSidebar = (props: DesktopSidebarProps) => {\n const { sidebarConfig } = useContext(SidebarConfigContext);\n const {\n openDelayMs = sidebarConfig.defaultOpenDelayMs,\n closeDelayMs = sidebarConfig.defaultCloseDelayMs,\n disableExpandOnHover,\n children,\n } = props;\n\n const classes = useStyles({ sidebarConfig });\n const isSmallScreen = useMediaQuery<Theme>(\n theme => theme.breakpoints.down('md'),\n { noSsr: true },\n );\n const [state, setState] = useState(State.Closed);\n const hoverTimerRef = useRef<number>();\n const { isPinned, toggleSidebarPinState } = useSidebarPinState();\n\n const handleOpen = () => {\n if (isPinned || disableExpandOnHover) {\n return;\n }\n if (hoverTimerRef.current) {\n clearTimeout(hoverTimerRef.current);\n hoverTimerRef.current = undefined;\n }\n if (state !== State.Open && !isSmallScreen) {\n hoverTimerRef.current = window.setTimeout(() => {\n hoverTimerRef.current = undefined;\n setState(State.Open);\n }, openDelayMs);\n\n setState(State.Idle);\n }\n };\n\n const handleClose = () => {\n if (isPinned || disableExpandOnHover) {\n return;\n }\n if (hoverTimerRef.current) {\n clearTimeout(hoverTimerRef.current);\n hoverTimerRef.current = undefined;\n }\n if (state === State.Idle) {\n setState(State.Closed);\n } else if (state === State.Open) {\n hoverTimerRef.current = window.setTimeout(() => {\n hoverTimerRef.current = undefined;\n setState(State.Closed);\n }, closeDelayMs);\n }\n };\n\n const isOpen = (state === State.Open && !isSmallScreen) || isPinned;\n\n /**\n * Close/Open Sidebar directly without delays. Also toggles `SidebarPinState` to avoid hidden content behind Sidebar.\n */\n const setOpen = (open: boolean) => {\n if (open) {\n setState(State.Open);\n toggleSidebarPinState();\n } else {\n setState(State.Closed);\n toggleSidebarPinState();\n }\n };\n\n return (\n <nav style={{}} aria-label=\"sidebar nav\">\n <A11ySkipSidebar />\n <SidebarOpenStateProvider value={{ isOpen, setOpen }}>\n <Box\n className={classes.root}\n data-testid=\"sidebar-root\"\n onMouseEnter={disableExpandOnHover ? () => {} : handleOpen}\n onFocus={disableExpandOnHover ? () => {} : handleOpen}\n onMouseLeave={disableExpandOnHover ? () => {} : handleClose}\n onBlur={disableExpandOnHover ? () => {} : handleClose}\n >\n <Box\n className={classnames(classes.drawer, classes.drawerWidth, {\n [classes.drawerOpen]: isOpen,\n })}\n >\n {children}\n </Box>\n </Box>\n </SidebarOpenStateProvider>\n </nav>\n );\n};\n\n/**\n * Passing children into the desktop or mobile sidebar depending on the context\n *\n * @public\n */\nexport const Sidebar = (props: SidebarProps) => {\n const sidebarConfig: SidebarConfig = makeSidebarConfig(\n props.sidebarOptions ?? {},\n );\n const submenuConfig: SubmenuConfig = makeSidebarSubmenuConfig(\n props.submenuOptions ?? {},\n );\n const { children, disableExpandOnHover, openDelayMs, closeDelayMs } = props;\n const { isMobile } = useSidebarPinState();\n\n return isMobile ? (\n <MobileSidebar>{children}</MobileSidebar>\n ) : (\n <SidebarConfigContext.Provider value={{ sidebarConfig, submenuConfig }}>\n <DesktopSidebar\n openDelayMs={openDelayMs}\n closeDelayMs={closeDelayMs}\n disableExpandOnHover={disableExpandOnHover}\n >\n {children}\n </DesktopSidebar>\n </SidebarConfigContext.Provider>\n );\n};\n\nfunction A11ySkipSidebar() {\n const { sidebarConfig } = useContext(SidebarConfigContext);\n const { focusContent, contentRef } = useContent();\n const classes = useStyles({ sidebarConfig });\n const { t } = useTranslationRef(coreComponentsTranslationRef);\n\n if (!contentRef?.current) {\n return null;\n }\n return (\n <Button\n onClick={focusContent}\n variant=\"contained\"\n className={classnames(classes.visuallyHidden)}\n >\n {t('skipToContent')}\n </Button>\n );\n}\n"],"names":["classnames"],"mappings":"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"}
1
+ {"version":3,"file":"Bar.esm.js","sources":["../../../src/layout/Sidebar/Bar.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 Button from '@material-ui/core/Button';\nimport { makeStyles, Theme } from '@material-ui/core/styles';\nimport useMediaQuery from '@material-ui/core/useMediaQuery';\nimport classnames from 'classnames';\nimport { ReactNode, useContext, useRef, useState } from 'react';\n\nimport {\n makeSidebarConfig,\n makeSidebarSubmenuConfig,\n SidebarConfig,\n SidebarConfigContext,\n SidebarOptions,\n SubmenuConfig,\n SubmenuOptions,\n} from './config';\nimport { MobileSidebar } from './MobileSidebar';\nimport { useContent } from './Page';\nimport { SidebarOpenStateProvider } from './SidebarOpenStateContext';\nimport { useSidebarPinState } from './SidebarPinStateContext';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { coreComponentsTranslationRef } from '../../translation';\n\n/** @public */\nexport type SidebarClassKey = 'drawer' | 'drawerOpen';\nconst useStyles = makeStyles<Theme, { sidebarConfig: SidebarConfig }>(\n theme => ({\n root: {\n left: 0,\n top: 0,\n bottom: 0,\n zIndex: theme.zIndex.appBar,\n position: 'fixed',\n },\n drawer: {\n display: 'flex',\n flexFlow: 'column nowrap',\n alignItems: 'flex-start',\n left: 0,\n top: 0,\n bottom: 0,\n position: 'absolute',\n background: theme.palette.navigation.background,\n overflowX: 'hidden',\n msOverflowStyle: 'none',\n scrollbarWidth: 'none',\n transition: theme.transitions.create('width', {\n easing: theme.transitions.easing.sharp,\n duration: theme.transitions.duration.shortest,\n }),\n '& > *': {\n flexShrink: 0,\n },\n '&::-webkit-scrollbar': {\n display: 'none',\n },\n '@media print': {\n display: 'none',\n },\n },\n drawerWidth: props => ({\n width: props.sidebarConfig.drawerWidthClosed,\n }),\n drawerOpen: props => ({\n width: props.sidebarConfig.drawerWidthOpen,\n transition: theme.transitions.create('width', {\n easing: theme.transitions.easing.sharp,\n duration: theme.transitions.duration.shorter,\n }),\n }),\n visuallyHidden: {\n top: 0,\n position: 'absolute',\n zIndex: 1000,\n transform: 'translateY(-200%)',\n '&:focus': {\n transform: 'translateY(5px)',\n },\n },\n }),\n { name: 'BackstageSidebar' },\n);\n\nenum State {\n Closed,\n Idle,\n Open,\n}\n\n/** @public */\nexport type SidebarProps = {\n openDelayMs?: number;\n closeDelayMs?: number;\n sidebarOptions?: SidebarOptions;\n submenuOptions?: SubmenuOptions;\n disableExpandOnHover?: boolean;\n children?: ReactNode;\n};\n\nexport type DesktopSidebarProps = {\n openDelayMs?: number;\n closeDelayMs?: number;\n disableExpandOnHover?: boolean;\n children?: ReactNode;\n};\n\n/**\n * Places the Sidebar & wraps the children providing context weather the `Sidebar` is open or not.\n *\n * Handles & delays hover events for expanding the `Sidebar`\n *\n * @param props `disableExpandOnHover` disables the default hover behaviour;\n * `openDelayMs` & `closeDelayMs` set delay until sidebar will open/close on hover\n * @returns\n * @internal\n */\nconst DesktopSidebar = (props: DesktopSidebarProps) => {\n const { sidebarConfig } = useContext(SidebarConfigContext);\n const {\n openDelayMs = sidebarConfig.defaultOpenDelayMs,\n closeDelayMs = sidebarConfig.defaultCloseDelayMs,\n disableExpandOnHover,\n children,\n } = props;\n\n const classes = useStyles({ sidebarConfig });\n const isSmallScreen = useMediaQuery<Theme>(\n theme => theme.breakpoints.down('md'),\n { noSsr: true },\n );\n const [state, setState] = useState(State.Closed);\n const hoverTimerRef = useRef<number>();\n const { isPinned, toggleSidebarPinState } = useSidebarPinState();\n\n const handleOpen = () => {\n if (isPinned || disableExpandOnHover) {\n return;\n }\n if (hoverTimerRef.current) {\n clearTimeout(hoverTimerRef.current);\n hoverTimerRef.current = undefined;\n }\n if (state !== State.Open && !isSmallScreen) {\n hoverTimerRef.current = window.setTimeout(() => {\n hoverTimerRef.current = undefined;\n setState(State.Open);\n }, openDelayMs);\n\n setState(State.Idle);\n }\n };\n\n const handleClose = () => {\n if (isPinned || disableExpandOnHover) {\n return;\n }\n if (hoverTimerRef.current) {\n clearTimeout(hoverTimerRef.current);\n hoverTimerRef.current = undefined;\n }\n if (state === State.Idle) {\n setState(State.Closed);\n } else if (state === State.Open) {\n hoverTimerRef.current = window.setTimeout(() => {\n hoverTimerRef.current = undefined;\n setState(State.Closed);\n }, closeDelayMs);\n }\n };\n\n const isOpen = (state === State.Open && !isSmallScreen) || isPinned;\n\n /**\n * Close/Open Sidebar directly without delays. Also toggles `SidebarPinState` to avoid hidden content behind Sidebar.\n */\n const setOpen = (open: boolean) => {\n if (open) {\n setState(State.Open);\n toggleSidebarPinState();\n } else {\n setState(State.Closed);\n toggleSidebarPinState();\n }\n };\n\n return (\n <nav style={{}} aria-label=\"sidebar nav\">\n <A11ySkipSidebar />\n <SidebarOpenStateProvider value={{ isOpen, setOpen }}>\n <Box\n className={classes.root}\n data-testid=\"sidebar-root\"\n onMouseEnter={disableExpandOnHover ? () => {} : handleOpen}\n onFocus={disableExpandOnHover ? () => {} : handleOpen}\n onMouseLeave={disableExpandOnHover ? () => {} : handleClose}\n onBlur={disableExpandOnHover ? () => {} : handleClose}\n >\n <Box\n className={classnames(classes.drawer, classes.drawerWidth, {\n [classes.drawerOpen]: isOpen,\n })}\n >\n {children}\n </Box>\n </Box>\n </SidebarOpenStateProvider>\n </nav>\n );\n};\n\n/**\n * Passing children into the desktop or mobile sidebar depending on the context\n *\n * @public\n */\nexport const Sidebar = (props: SidebarProps) => {\n const sidebarConfig: SidebarConfig = makeSidebarConfig(\n props.sidebarOptions ?? {},\n );\n const submenuConfig: SubmenuConfig = makeSidebarSubmenuConfig(\n props.submenuOptions ?? {},\n );\n const { children, disableExpandOnHover, openDelayMs, closeDelayMs } = props;\n const { isMobile } = useSidebarPinState();\n\n return isMobile ? (\n <MobileSidebar>{children}</MobileSidebar>\n ) : (\n <SidebarConfigContext.Provider value={{ sidebarConfig, submenuConfig }}>\n <DesktopSidebar\n openDelayMs={openDelayMs}\n closeDelayMs={closeDelayMs}\n disableExpandOnHover={disableExpandOnHover}\n >\n {children}\n </DesktopSidebar>\n </SidebarConfigContext.Provider>\n );\n};\n\nfunction A11ySkipSidebar() {\n const { sidebarConfig } = useContext(SidebarConfigContext);\n const { focusContent, contentRef } = useContent();\n const classes = useStyles({ sidebarConfig });\n const { t } = useTranslationRef(coreComponentsTranslationRef);\n\n if (!contentRef?.current) {\n return null;\n }\n return (\n <Button\n onClick={focusContent}\n variant=\"contained\"\n className={classnames(classes.visuallyHidden)}\n >\n {t('skipToContent')}\n </Button>\n );\n}\n"],"names":["classnames"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/core-components",
3
- "version": "0.17.1",
3
+ "version": "0.17.2-next.0",
4
4
  "description": "Core components used by Backstage plugins and apps",
5
5
  "backstage": {
6
6
  "role": "web-library"
@@ -66,11 +66,11 @@
66
66
  "test": "backstage-cli package test"
67
67
  },
68
68
  "dependencies": {
69
- "@backstage/config": "^1.3.2",
70
- "@backstage/core-plugin-api": "^1.10.6",
71
- "@backstage/errors": "^1.2.7",
72
- "@backstage/theme": "^0.6.5",
73
- "@backstage/version-bridge": "^1.0.11",
69
+ "@backstage/config": "1.3.2",
70
+ "@backstage/core-plugin-api": "1.10.6",
71
+ "@backstage/errors": "1.2.7",
72
+ "@backstage/theme": "0.6.6-next.0",
73
+ "@backstage/version-bridge": "1.0.11",
74
74
  "@dagrejs/dagre": "^1.1.4",
75
75
  "@date-io/core": "^1.3.13",
76
76
  "@material-table/core": "^3.1.0",
@@ -106,10 +106,10 @@
106
106
  "zod": "^3.22.4"
107
107
  },
108
108
  "devDependencies": {
109
- "@backstage/app-defaults": "^1.6.1",
110
- "@backstage/cli": "^0.32.0",
111
- "@backstage/core-app-api": "^1.16.1",
112
- "@backstage/test-utils": "^1.7.7",
109
+ "@backstage/app-defaults": "1.6.2-next.0",
110
+ "@backstage/cli": "0.32.1-next.1",
111
+ "@backstage/core-app-api": "1.16.1",
112
+ "@backstage/test-utils": "1.7.8-next.0",
113
113
  "@testing-library/dom": "^10.0.0",
114
114
  "@testing-library/jest-dom": "^6.0.0",
115
115
  "@testing-library/user-event": "^14.0.0",