@red-hat-developer-hub/backstage-plugin-global-floating-action-button 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ # @red-hat-developer-hub/backstage-plugin-global-floating-action-button
2
+
3
+ ## 0.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - bae2778: adds the global floating button plugin
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Global floating action button for Backstage
2
+
3
+ This plugin enables you to add a floating button, with or without submenu options, to your desired pages.
4
+
5
+ ## Getting started
6
+
7
+ This plugin has been added to the example app in this workspace, meaning it can be accessed by running `yarn start` from this directory, and then navigating to [/test-global-floating-action](http://localhost:3000/test-global-floating-action).
8
+
9
+ ## For administrators
10
+
11
+ ### Installation
12
+
13
+ #### Procedure
14
+
15
+ 1. Install the Global floating action button plugin using the following command:
16
+
17
+ ```console
18
+ yarn workspace app add @red-hat-developer-hub/backstage-plugin-global-floating-action-button
19
+ ```
20
+
21
+ 2. Add **Bulk import** Sidebar Item in `packages/app/src/components/Root/Root.tsx`:
22
+
23
+ ```tsx title="packages/app/src/components/Root/Root.tsx"
24
+ /* highlight-add-next-line */
25
+ import { GlobalFloatingButton } from '@red-hat-developer-hub/backstage-plugin-global-floating-action-button';
26
+
27
+ export const Root = ({ children }: PropsWithChildren<{}>) => (
28
+ <SidebarPage>
29
+ ... /* highlight-add-start */
30
+ <GlobalFloatingButton
31
+ floatingButtons={[
32
+ {
33
+ color: 'success',
34
+ icon: <CreateComponentIcon />,
35
+ label: 'Create',
36
+ toolTip: 'Create entity',
37
+ to: '/create',
38
+ },
39
+ {
40
+ icon: <LibraryBooks />,
41
+ label: 'Docs',
42
+ toolTip: 'Docs',
43
+ to: '/docs',
44
+ position: 'bottom-center',
45
+ },
46
+ ]}
47
+ />
48
+ /* highlight-add-end */ ...
49
+ </SidebarPage>
50
+ );
51
+ ```
@@ -0,0 +1,42 @@
1
+ import * as React from 'react';
2
+ import { useNavigate } from 'react-router-dom';
3
+ import Fab from '@mui/material/Fab';
4
+ import Tooltip from '@mui/material/Tooltip';
5
+ import Typography from '@mui/material/Typography';
6
+ import { FabIcon } from './FabIcon.esm.js';
7
+ import { Slot } from '../types.esm.js';
8
+
9
+ const FAB = ({
10
+ actionButton,
11
+ size
12
+ }) => {
13
+ const navigate = useNavigate();
14
+ const isExternalUri = (uri) => /^([a-z+.-]+):/.test(uri);
15
+ const external = isExternalUri(actionButton.to);
16
+ const newWindow = external && !!/^https?:/.exec(actionButton.to);
17
+ const navigateTo = () => actionButton.to && !external ? navigate(actionButton.to) : "";
18
+ return /* @__PURE__ */ React.createElement(
19
+ Tooltip,
20
+ {
21
+ title: actionButton.toolTip,
22
+ placement: Slot.PAGE_END ? "left" : "right"
23
+ },
24
+ /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
25
+ Fab,
26
+ {
27
+ ...newWindow ? { target: "_blank", rel: "noopener" } : {},
28
+ variant: actionButton.showLabel || !actionButton.icon ? "extended" : "circular",
29
+ size: size || actionButton.size || "medium",
30
+ color: actionButton.color || "default",
31
+ "aria-label": actionButton.label,
32
+ onClick: actionButton.onClick || navigateTo,
33
+ ...external ? { href: actionButton.to } : {}
34
+ },
35
+ actionButton.icon && /* @__PURE__ */ React.createElement(FabIcon, { icon: actionButton.icon }),
36
+ (actionButton.showLabel || !actionButton.icon) && /* @__PURE__ */ React.createElement(Typography, { sx: { ml: 1 } }, actionButton.label)
37
+ ))
38
+ );
39
+ };
40
+
41
+ export { FAB };
42
+ //# sourceMappingURL=FAB.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FAB.esm.js","sources":["../../src/components/FAB.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as React from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport Fab from '@mui/material/Fab';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport { FabIcon } from './FabIcon';\nimport { FloatingActionButton, Slot } from '../types';\n\nexport const FAB = ({\n actionButton,\n size,\n}: {\n actionButton: FloatingActionButton;\n size?: 'small' | 'medium' | 'large';\n}) => {\n const navigate = useNavigate();\n const isExternalUri = (uri: string) => /^([a-z+.-]+):/.test(uri);\n const external = isExternalUri(actionButton.to!);\n const newWindow = external && !!/^https?:/.exec(actionButton.to!);\n const navigateTo = () =>\n actionButton.to && !external ? navigate(actionButton.to) : '';\n return (\n <Tooltip\n title={actionButton.toolTip}\n placement={Slot.PAGE_END ? 'left' : 'right'}\n >\n <div>\n <Fab\n {...(newWindow ? { target: '_blank', rel: 'noopener' } : {})}\n variant={\n actionButton.showLabel || !actionButton.icon\n ? 'extended'\n : 'circular'\n }\n size={size || actionButton.size || 'medium'}\n color={actionButton.color || 'default'}\n aria-label={actionButton.label}\n onClick={actionButton.onClick || navigateTo}\n {...(external ? { href: actionButton.to } : {})}\n >\n {actionButton.icon && <FabIcon icon={actionButton.icon} />}\n {(actionButton.showLabel || !actionButton.icon) && (\n <Typography sx={{ ml: 1 }}>{actionButton.label}</Typography>\n )}\n </Fab>\n </div>\n </Tooltip>\n );\n};\n"],"names":[],"mappings":";;;;;;;;AAwBO,MAAM,MAAM,CAAC;AAAA,EAClB,YAAA;AAAA,EACA;AACF,CAGM,KAAA;AACJ,EAAA,MAAM,WAAW,WAAY,EAAA;AAC7B,EAAA,MAAM,aAAgB,GAAA,CAAC,GAAgB,KAAA,eAAA,CAAgB,KAAK,GAAG,CAAA;AAC/D,EAAM,MAAA,QAAA,GAAW,aAAc,CAAA,YAAA,CAAa,EAAG,CAAA;AAC/C,EAAA,MAAM,YAAY,QAAY,IAAA,CAAC,CAAC,UAAW,CAAA,IAAA,CAAK,aAAa,EAAG,CAAA;AAChE,EAAM,MAAA,UAAA,GAAa,MACjB,YAAa,CAAA,EAAA,IAAM,CAAC,QAAW,GAAA,QAAA,CAAS,YAAa,CAAA,EAAE,CAAI,GAAA,EAAA;AAC7D,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACC,OAAO,YAAa,CAAA,OAAA;AAAA,MACpB,SAAA,EAAW,IAAK,CAAA,QAAA,GAAW,MAAS,GAAA;AAAA,KAAA;AAAA,wCAEnC,KACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,MAAC,GAAA;AAAA,MAAA;AAAA,QACE,GAAI,YAAY,EAAE,MAAA,EAAQ,UAAU,GAAK,EAAA,UAAA,KAAe,EAAC;AAAA,QAC1D,SACE,YAAa,CAAA,SAAA,IAAa,CAAC,YAAA,CAAa,OACpC,UACA,GAAA,UAAA;AAAA,QAEN,IAAA,EAAM,IAAQ,IAAA,YAAA,CAAa,IAAQ,IAAA,QAAA;AAAA,QACnC,KAAA,EAAO,aAAa,KAAS,IAAA,SAAA;AAAA,QAC7B,cAAY,YAAa,CAAA,KAAA;AAAA,QACzB,OAAA,EAAS,aAAa,OAAW,IAAA,UAAA;AAAA,QAChC,GAAI,QAAW,GAAA,EAAE,MAAM,YAAa,CAAA,EAAA,KAAO;AAAC,OAAA;AAAA,MAE5C,aAAa,IAAQ,oBAAA,KAAA,CAAA,aAAA,CAAC,OAAQ,EAAA,EAAA,IAAA,EAAM,aAAa,IAAM,EAAA,CAAA;AAAA,MAAA,CACtD,YAAa,CAAA,SAAA,IAAa,CAAC,YAAA,CAAa,IACxC,qBAAA,KAAA,CAAA,aAAA,CAAC,UAAW,EAAA,EAAA,EAAA,EAAI,EAAE,EAAA,EAAI,CAAE,EAAA,EAAA,EAAI,aAAa,KAAM;AAAA,KAGrD;AAAA,GACF;AAEJ;;;;"}
@@ -0,0 +1,52 @@
1
+ import * as React from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+ import Fab from '@mui/material/Fab';
4
+ import Tooltip from '@mui/material/Tooltip';
5
+ import { useTheme } from '@mui/material/styles';
6
+ import CloseIcon from '@mui/icons-material/Close';
7
+ import MenuIcon from '@mui/icons-material/Menu';
8
+ import Slide from '@mui/material/Slide';
9
+ import { FAB } from './FAB.esm.js';
10
+
11
+ const FABWithSubmenu = ({
12
+ fabs,
13
+ ref
14
+ }) => {
15
+ const theme = useTheme();
16
+ const { pathname } = useLocation();
17
+ const [isMenuOpen, setIsMenuOpen] = React.useState(false);
18
+ React.useEffect(() => {
19
+ return () => {
20
+ setIsMenuOpen(false);
21
+ };
22
+ }, [pathname]);
23
+ const handleClick = () => {
24
+ setIsMenuOpen(!isMenuOpen);
25
+ };
26
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Tooltip, { title: "Menu", placement: "left" }, /* @__PURE__ */ React.createElement(
27
+ Fab,
28
+ {
29
+ size: "medium",
30
+ color: "default",
31
+ onClick: handleClick,
32
+ "aria-label": "Menu",
33
+ variant: "circular"
34
+ },
35
+ isMenuOpen ? /* @__PURE__ */ React.createElement(CloseIcon, null) : /* @__PURE__ */ React.createElement(MenuIcon, null)
36
+ )), isMenuOpen && /* @__PURE__ */ React.createElement(
37
+ Slide,
38
+ {
39
+ container: ref,
40
+ easing: {
41
+ enter: theme.transitions.easing.easeOut,
42
+ exit: theme.transitions.easing.sharp
43
+ }
44
+ },
45
+ /* @__PURE__ */ React.createElement(React.Fragment, null, fabs?.map((fb) => {
46
+ return /* @__PURE__ */ React.createElement(FAB, { actionButton: fb, size: "medium", key: fb.label });
47
+ }))
48
+ ));
49
+ };
50
+
51
+ export { FABWithSubmenu };
52
+ //# sourceMappingURL=FABWithSubmenu.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FABWithSubmenu.esm.js","sources":["../../src/components/FABWithSubmenu.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as React from 'react';\nimport { useLocation } from 'react-router-dom';\nimport Fab from '@mui/material/Fab';\nimport Tooltip from '@mui/material/Tooltip';\nimport { useTheme } from '@mui/material/styles';\nimport CloseIcon from '@mui/icons-material/Close';\nimport MenuIcon from '@mui/icons-material/Menu';\nimport Slide from '@mui/material/Slide';\nimport { FloatingActionButton } from '../types';\nimport { FAB } from './FAB';\n\nexport const FABWithSubmenu = ({\n fabs,\n ref,\n}: {\n fabs: FloatingActionButton[];\n ref: HTMLDivElement | null;\n}) => {\n const theme = useTheme();\n const { pathname } = useLocation();\n const [isMenuOpen, setIsMenuOpen] = React.useState(false);\n\n React.useEffect(() => {\n return () => {\n setIsMenuOpen(false);\n };\n }, [pathname]);\n\n const handleClick = () => {\n setIsMenuOpen(!isMenuOpen);\n };\n return (\n <>\n <Tooltip title=\"Menu\" placement=\"left\">\n <Fab\n size=\"medium\"\n color=\"default\"\n onClick={handleClick}\n aria-label=\"Menu\"\n variant=\"circular\"\n >\n {isMenuOpen ? <CloseIcon /> : <MenuIcon />}\n </Fab>\n </Tooltip>\n {isMenuOpen && (\n <Slide\n container={ref}\n easing={{\n enter: theme.transitions.easing.easeOut,\n exit: theme.transitions.easing.sharp,\n }}\n >\n <>\n {fabs?.map(fb => {\n return <FAB actionButton={fb} size=\"medium\" key={fb.label} />;\n })}\n </>\n </Slide>\n )}\n </>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;AA2BO,MAAM,iBAAiB,CAAC;AAAA,EAC7B,IAAA;AAAA,EACA;AACF,CAGM,KAAA;AACJ,EAAA,MAAM,QAAQ,QAAS,EAAA;AACvB,EAAM,MAAA,EAAE,QAAS,EAAA,GAAI,WAAY,EAAA;AACjC,EAAA,MAAM,CAAC,UAAY,EAAA,aAAa,CAAI,GAAA,KAAA,CAAM,SAAS,KAAK,CAAA;AAExD,EAAA,KAAA,CAAM,UAAU,MAAM;AACpB,IAAA,OAAO,MAAM;AACX,MAAA,aAAA,CAAc,KAAK,CAAA;AAAA,KACrB;AAAA,GACF,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,aAAA,CAAc,CAAC,UAAU,CAAA;AAAA,GAC3B;AACA,EAAA,iFAEK,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,EAAQ,KAAM,EAAA,MAAA,EAAO,WAAU,MAC9B,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,IAAK,EAAA,QAAA;AAAA,MACL,KAAM,EAAA,SAAA;AAAA,MACN,OAAS,EAAA,WAAA;AAAA,MACT,YAAW,EAAA,MAAA;AAAA,MACX,OAAQ,EAAA;AAAA,KAAA;AAAA,IAEP,UAAa,mBAAA,KAAA,CAAA,aAAA,CAAC,SAAU,EAAA,IAAA,CAAA,uCAAM,QAAS,EAAA,IAAA;AAAA,GAE5C,GACC,UACC,oBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAW,EAAA,GAAA;AAAA,MACX,MAAQ,EAAA;AAAA,QACN,KAAA,EAAO,KAAM,CAAA,WAAA,CAAY,MAAO,CAAA,OAAA;AAAA,QAChC,IAAA,EAAM,KAAM,CAAA,WAAA,CAAY,MAAO,CAAA;AAAA;AACjC,KAAA;AAAA,oBAEA,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EACG,IAAM,EAAA,GAAA,CAAI,CAAM,EAAA,KAAA;AACf,MAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,OAAI,YAAc,EAAA,EAAA,EAAI,MAAK,QAAS,EAAA,GAAA,EAAK,GAAG,KAAO,EAAA,CAAA;AAAA,KAC5D,CACH;AAAA,GAGN,CAAA;AAEJ;;;;"}
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+ import { useApp } from '@backstage/core-plugin-api';
3
+ import MuiIcon from '@mui/material/Icon';
4
+
5
+ const FabIcon = ({ icon }) => {
6
+ const app = useApp();
7
+ if (!icon) {
8
+ return null;
9
+ }
10
+ if (React.isValidElement(icon)) {
11
+ return icon;
12
+ }
13
+ const strIcon = icon;
14
+ const SystemIcon = app.getSystemIcon(strIcon);
15
+ if (SystemIcon) {
16
+ return /* @__PURE__ */ React.createElement(SystemIcon, null);
17
+ }
18
+ if (strIcon.startsWith("<svg")) {
19
+ const svgDataUri = `data:image/svg+xml;base64,${btoa(strIcon)}`;
20
+ return /* @__PURE__ */ React.createElement(MuiIcon, null, /* @__PURE__ */ React.createElement("img", { src: svgDataUri, alt: "" }));
21
+ }
22
+ if (strIcon.startsWith("https://") || strIcon.startsWith("http://") || strIcon.startsWith("/")) {
23
+ return /* @__PURE__ */ React.createElement(MuiIcon, { baseClassName: "material-icons-outlined" }, /* @__PURE__ */ React.createElement("img", { src: strIcon, alt: "" }));
24
+ }
25
+ return /* @__PURE__ */ React.createElement(MuiIcon, { baseClassName: "material-icons-outlined" }, strIcon);
26
+ };
27
+
28
+ export { FabIcon };
29
+ //# sourceMappingURL=FabIcon.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FabIcon.esm.js","sources":["../../src/components/FabIcon.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport * as React from 'react';\nimport { useApp } from '@backstage/core-plugin-api';\n\nimport MuiIcon from '@mui/material/Icon';\n\nexport const FabIcon = ({ icon }: { icon: string | React.ReactElement }) => {\n const app = useApp();\n\n if (!icon) {\n return null;\n }\n\n if (React.isValidElement(icon)) {\n return icon;\n }\n\n const strIcon = icon as string;\n\n const SystemIcon = app.getSystemIcon(strIcon);\n\n if (SystemIcon) {\n return <SystemIcon />;\n }\n\n if (strIcon.startsWith('<svg')) {\n const svgDataUri = `data:image/svg+xml;base64,${btoa(strIcon)}`;\n return (\n <MuiIcon>\n <img src={svgDataUri} alt=\"\" />\n </MuiIcon>\n );\n }\n\n if (\n strIcon.startsWith('https://') ||\n strIcon.startsWith('http://') ||\n strIcon.startsWith('/')\n ) {\n return (\n <MuiIcon baseClassName=\"material-icons-outlined\">\n <img src={strIcon} alt=\"\" />\n </MuiIcon>\n );\n }\n\n return <MuiIcon baseClassName=\"material-icons-outlined\">{strIcon}</MuiIcon>;\n};\n"],"names":[],"mappings":";;;;AAoBO,MAAM,OAAU,GAAA,CAAC,EAAE,IAAA,EAAkD,KAAA;AAC1E,EAAA,MAAM,MAAM,MAAO,EAAA;AAEnB,EAAA,IAAI,CAAC,IAAM,EAAA;AACT,IAAO,OAAA,IAAA;AAAA;AAGT,EAAI,IAAA,KAAA,CAAM,cAAe,CAAA,IAAI,CAAG,EAAA;AAC9B,IAAO,OAAA,IAAA;AAAA;AAGT,EAAA,MAAM,OAAU,GAAA,IAAA;AAEhB,EAAM,MAAA,UAAA,GAAa,GAAI,CAAA,aAAA,CAAc,OAAO,CAAA;AAE5C,EAAA,IAAI,UAAY,EAAA;AACd,IAAA,2CAAQ,UAAW,EAAA,IAAA,CAAA;AAAA;AAGrB,EAAI,IAAA,OAAA,CAAQ,UAAW,CAAA,MAAM,CAAG,EAAA;AAC9B,IAAA,MAAM,UAAa,GAAA,CAAA,0BAAA,EAA6B,IAAK,CAAA,OAAO,CAAC,CAAA,CAAA;AAC7D,IACE,uBAAA,KAAA,CAAA,aAAA,CAAC,+BACE,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAI,KAAK,UAAY,EAAA,GAAA,EAAI,IAAG,CAC/B,CAAA;AAAA;AAIJ,EACE,IAAA,OAAA,CAAQ,UAAW,CAAA,UAAU,CAC7B,IAAA,OAAA,CAAQ,UAAW,CAAA,SAAS,CAC5B,IAAA,OAAA,CAAQ,UAAW,CAAA,GAAG,CACtB,EAAA;AACA,IACE,uBAAA,KAAA,CAAA,aAAA,CAAC,OAAQ,EAAA,EAAA,aAAA,EAAc,yBACrB,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,SAAI,GAAK,EAAA,OAAA,EAAS,GAAI,EAAA,EAAA,EAAG,CAC5B,CAAA;AAAA;AAIJ,EAAA,uBAAQ,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,EAAQ,aAAc,EAAA,yBAAA,EAAA,EAA2B,OAAQ,CAAA;AACnE;;;;"}
@@ -0,0 +1,69 @@
1
+ import * as React from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+ import classnames from 'classnames';
4
+ import { makeStyles } from '@mui/styles';
5
+ import { FABWithSubmenu } from './FABWithSubmenu.esm.js';
6
+ import { FAB } from './FAB.esm.js';
7
+ import { filterAndSortButtons } from '../utils.esm.js';
8
+
9
+ const useStyles = makeStyles((theme) => ({
10
+ button: {
11
+ zIndex: 200,
12
+ display: "flex",
13
+ position: "fixed",
14
+ maxWidth: "150px",
15
+ gap: "10px"
16
+ },
17
+ "page-end": {
18
+ bottom: theme.spacing(4),
19
+ right: theme.spacing(4)
20
+ },
21
+ "bottom-center": {
22
+ bottom: theme.spacing(4),
23
+ left: "50%"
24
+ }
25
+ }));
26
+ const FloatingButton = ({
27
+ floatingButtons,
28
+ position
29
+ }) => {
30
+ const { pathname } = useLocation();
31
+ const subMenuRef = React.useRef(null);
32
+ const [subMenuDirection, setSubMenuDirection] = React.useState("column");
33
+ const fabButton = useStyles();
34
+ React.useEffect(() => {
35
+ const floatingButtonElement = document.getElementById("floating-button");
36
+ const screenHeight = window.innerHeight;
37
+ if (floatingButtonElement) {
38
+ const { top } = floatingButtonElement?.getBoundingClientRect();
39
+ if (top < screenHeight / 2) {
40
+ setSubMenuDirection("column");
41
+ } else {
42
+ setSubMenuDirection("column-reverse");
43
+ }
44
+ }
45
+ }, [pathname]);
46
+ const fabs = React.useMemo(
47
+ () => filterAndSortButtons(floatingButtons, pathname),
48
+ [floatingButtons, pathname]
49
+ );
50
+ if (fabs?.length === 0) {
51
+ return null;
52
+ }
53
+ return /* @__PURE__ */ React.createElement(
54
+ "div",
55
+ {
56
+ className: classnames(fabButton.button, fabButton[position]),
57
+ style: {
58
+ flexDirection: subMenuDirection || "column-reverse"
59
+ },
60
+ id: "floating-button",
61
+ "data-testId": "floating-button",
62
+ ref: subMenuRef
63
+ },
64
+ fabs.length > 1 ? /* @__PURE__ */ React.createElement(FABWithSubmenu, { fabs, ref: subMenuRef.current }) : /* @__PURE__ */ React.createElement(FAB, { actionButton: fabs[0] })
65
+ );
66
+ };
67
+
68
+ export { FloatingButton };
69
+ //# sourceMappingURL=FloatingButton.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FloatingButton.esm.js","sources":["../../src/components/FloatingButton.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as React from 'react';\nimport { useLocation } from 'react-router-dom';\nimport classnames from 'classnames';\n\nimport { makeStyles } from '@mui/styles';\nimport { FABWithSubmenu } from './FABWithSubmenu';\nimport { FAB } from './FAB';\nimport { FlexDirection, FloatingActionButton, Slot } from '../types';\nimport { filterAndSortButtons } from '../utils';\n\nconst useStyles = makeStyles(theme => ({\n button: {\n zIndex: 200,\n display: 'flex',\n position: 'fixed',\n maxWidth: '150px',\n gap: '10px',\n },\n 'page-end': {\n bottom: theme.spacing(4),\n right: theme.spacing(4),\n },\n 'bottom-center': {\n bottom: theme.spacing(4),\n left: '50%',\n },\n}));\n\nexport const FloatingButton = ({\n floatingButtons,\n position,\n}: {\n floatingButtons: FloatingActionButton[];\n position: Slot;\n}) => {\n const { pathname } = useLocation();\n const subMenuRef = React.useRef<HTMLDivElement>(null);\n const [subMenuDirection, setSubMenuDirection] =\n React.useState<FlexDirection>('column');\n const fabButton = useStyles();\n\n React.useEffect(() => {\n const floatingButtonElement = document.getElementById('floating-button');\n const screenHeight = window.innerHeight;\n if (floatingButtonElement) {\n const { top } = floatingButtonElement?.getBoundingClientRect();\n if (top < screenHeight / 2) {\n setSubMenuDirection('column');\n } else {\n setSubMenuDirection('column-reverse');\n }\n }\n }, [pathname]);\n\n const fabs = React.useMemo(\n () => filterAndSortButtons(floatingButtons, pathname),\n [floatingButtons, pathname],\n );\n\n if (fabs?.length === 0) {\n return null;\n }\n return (\n <div\n className={classnames(fabButton.button, fabButton[position])}\n style={{\n flexDirection: subMenuDirection || 'column-reverse',\n }}\n id=\"floating-button\"\n data-testId=\"floating-button\"\n ref={subMenuRef}\n >\n {fabs.length > 1 ? (\n <FABWithSubmenu fabs={fabs} ref={subMenuRef.current} />\n ) : (\n <FAB actionButton={fabs[0]} />\n )}\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;;;AA0BA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,MAAQ,EAAA;AAAA,IACN,MAAQ,EAAA,GAAA;AAAA,IACR,OAAS,EAAA,MAAA;AAAA,IACT,QAAU,EAAA,OAAA;AAAA,IACV,QAAU,EAAA,OAAA;AAAA,IACV,GAAK,EAAA;AAAA,GACP;AAAA,EACA,UAAY,EAAA;AAAA,IACV,MAAA,EAAQ,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IACvB,KAAA,EAAO,KAAM,CAAA,OAAA,CAAQ,CAAC;AAAA,GACxB;AAAA,EACA,eAAiB,EAAA;AAAA,IACf,MAAA,EAAQ,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IACvB,IAAM,EAAA;AAAA;AAEV,CAAE,CAAA,CAAA;AAEK,MAAM,iBAAiB,CAAC;AAAA,EAC7B,eAAA;AAAA,EACA;AACF,CAGM,KAAA;AACJ,EAAM,MAAA,EAAE,QAAS,EAAA,GAAI,WAAY,EAAA;AACjC,EAAM,MAAA,UAAA,GAAa,KAAM,CAAA,MAAA,CAAuB,IAAI,CAAA;AACpD,EAAA,MAAM,CAAC,gBAAkB,EAAA,mBAAmB,CAC1C,GAAA,KAAA,CAAM,SAAwB,QAAQ,CAAA;AACxC,EAAA,MAAM,YAAY,SAAU,EAAA;AAE5B,EAAA,KAAA,CAAM,UAAU,MAAM;AACpB,IAAM,MAAA,qBAAA,GAAwB,QAAS,CAAA,cAAA,CAAe,iBAAiB,CAAA;AACvE,IAAA,MAAM,eAAe,MAAO,CAAA,WAAA;AAC5B,IAAA,IAAI,qBAAuB,EAAA;AACzB,MAAA,MAAM,EAAE,GAAA,EAAQ,GAAA,qBAAA,EAAuB,qBAAsB,EAAA;AAC7D,MAAI,IAAA,GAAA,GAAM,eAAe,CAAG,EAAA;AAC1B,QAAA,mBAAA,CAAoB,QAAQ,CAAA;AAAA,OACvB,MAAA;AACL,QAAA,mBAAA,CAAoB,gBAAgB,CAAA;AAAA;AACtC;AACF,GACF,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,MAAM,OAAO,KAAM,CAAA,OAAA;AAAA,IACjB,MAAM,oBAAqB,CAAA,eAAA,EAAiB,QAAQ,CAAA;AAAA,IACpD,CAAC,iBAAiB,QAAQ;AAAA,GAC5B;AAEA,EAAI,IAAA,IAAA,EAAM,WAAW,CAAG,EAAA;AACtB,IAAO,OAAA,IAAA;AAAA;AAET,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,WAAW,UAAW,CAAA,SAAA,CAAU,MAAQ,EAAA,SAAA,CAAU,QAAQ,CAAC,CAAA;AAAA,MAC3D,KAAO,EAAA;AAAA,QACL,eAAe,gBAAoB,IAAA;AAAA,OACrC;AAAA,MACA,EAAG,EAAA,iBAAA;AAAA,MACH,aAAY,EAAA,iBAAA;AAAA,MACZ,GAAK,EAAA;AAAA,KAAA;AAAA,IAEJ,IAAK,CAAA,MAAA,GAAS,CACb,mBAAA,KAAA,CAAA,aAAA,CAAC,kBAAe,IAAY,EAAA,GAAA,EAAK,UAAW,CAAA,OAAA,EAAS,oBAEpD,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAI,YAAc,EAAA,IAAA,CAAK,CAAC,CAAG,EAAA;AAAA,GAEhC;AAEJ;;;;"}
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+ import { evaluateFloatingButtonsWithPositions } from '../utils.esm.js';
3
+ import { FloatingButton } from './FloatingButton.esm.js';
4
+
5
+ const GlobalFloatingActionButton = ({
6
+ floatingButtons
7
+ }) => {
8
+ const floatingButtonMap = evaluateFloatingButtonsWithPositions(floatingButtons);
9
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, floatingButtonMap.map((fb) => /* @__PURE__ */ React.createElement(FloatingButton, { position: fb.slot, floatingButtons: fb.actions })));
10
+ };
11
+
12
+ export { GlobalFloatingActionButton };
13
+ //# sourceMappingURL=GlobalFloatingActionButton.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GlobalFloatingActionButton.esm.js","sources":["../../src/components/GlobalFloatingActionButton.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport * as React from 'react';\nimport { FloatingActionButton } from '../types';\nimport { evaluateFloatingButtonsWithPositions } from '../utils';\nimport { FloatingButton } from './FloatingButton';\n\nexport const GlobalFloatingActionButton = ({\n floatingButtons,\n}: {\n floatingButtons: FloatingActionButton[];\n}) => {\n const floatingButtonMap =\n evaluateFloatingButtonsWithPositions(floatingButtons);\n\n return (\n <>\n {floatingButtonMap.map(fb => (\n <FloatingButton position={fb.slot} floatingButtons={fb.actions} />\n ))}\n </>\n );\n};\n"],"names":[],"mappings":";;;;AAoBO,MAAM,6BAA6B,CAAC;AAAA,EACzC;AACF,CAEM,KAAA;AACJ,EAAM,MAAA,iBAAA,GACJ,qCAAqC,eAAe,CAAA;AAEtD,EAAA,uBAEK,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EAAA,iBAAA,CAAkB,GAAI,CAAA,CAAA,EAAA,qBACpB,KAAA,CAAA,aAAA,CAAA,cAAA,EAAA,EAAe,QAAU,EAAA,EAAA,CAAG,IAAM,EAAA,eAAA,EAAiB,EAAG,CAAA,OAAA,EAAS,CACjE,CACH,CAAA;AAEJ;;;;"}
@@ -0,0 +1,64 @@
1
+ /// <reference types="react" />
2
+ import * as react from 'react';
3
+ import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
4
+
5
+ /**
6
+ * @public
7
+ * Slot
8
+ */
9
+ declare enum Slot {
10
+ /**
11
+ * Positions the floating action button in the bottom-right corner of the page
12
+ */
13
+ PAGE_END = "page-end",
14
+ /**
15
+ * Positions the floating action button at the bottom center of the page
16
+ */
17
+ BOTTOM_CENTER = "bottom-center"
18
+ }
19
+ /**
20
+ * @public
21
+ * Floating Action Button With Positions
22
+ */
23
+ type FloatingActionButtonWithPositions = Array<{
24
+ slot: Slot;
25
+ actions: FloatingActionButton[];
26
+ }>;
27
+ /**
28
+ * @public
29
+ * Flex Direction
30
+ */
31
+ type FlexDirection = 'row' | 'row-reverse' | 'column' | 'column-reverse';
32
+ /**
33
+ * @public
34
+ * Floating Action Button
35
+ */
36
+ type FloatingActionButton = {
37
+ label: string;
38
+ showLabel?: boolean;
39
+ icon?: string | React.ReactElement;
40
+ size?: 'small' | 'medium' | 'large';
41
+ position?: Slot | string;
42
+ color?: 'default' | 'error' | 'info' | 'inherit' | 'primary' | 'secondary' | 'success' | 'warning';
43
+ onClick?: React.MouseEventHandler;
44
+ to?: string;
45
+ toolTip?: string;
46
+ priority?: number;
47
+ visibleOnPaths?: string[];
48
+ excludeOnPaths?: string[];
49
+ };
50
+
51
+ /**
52
+ * @public
53
+ * Global Floating Action Button Plugin
54
+ */
55
+ declare const globalFloatingActionButtonPlugin: _backstage_core_plugin_api.BackstagePlugin<{}, {}, {}>;
56
+ /**
57
+ * @public
58
+ * Global Floating Action Button
59
+ */
60
+ declare const GlobalFloatingActionButton: ({ floatingButtons, }: {
61
+ floatingButtons: FloatingActionButton[];
62
+ }) => react.JSX.Element;
63
+
64
+ export { type FlexDirection, type FloatingActionButton, type FloatingActionButtonWithPositions, GlobalFloatingActionButton, Slot, globalFloatingActionButtonPlugin };
@@ -0,0 +1,3 @@
1
+ export { GlobalFloatingActionButton, globalFloatingActionButtonPlugin } from './plugin.esm.js';
2
+ export { Slot } from './types.esm.js';
3
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,18 @@
1
+ import { createPlugin, createComponentExtension } from '@backstage/core-plugin-api';
2
+
3
+ const globalFloatingActionButtonPlugin = createPlugin({
4
+ id: "global-floating-action-button"
5
+ });
6
+ const GlobalFloatingActionButton = globalFloatingActionButtonPlugin.provide(
7
+ createComponentExtension({
8
+ name: "GlobalFloatingActionButton",
9
+ component: {
10
+ lazy: () => import('./components/GlobalFloatingActionButton.esm.js').then(
11
+ (m) => m.GlobalFloatingActionButton
12
+ )
13
+ }
14
+ })
15
+ );
16
+
17
+ export { GlobalFloatingActionButton, globalFloatingActionButtonPlugin };
18
+ //# sourceMappingURL=plugin.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n createComponentExtension,\n createPlugin,\n} from '@backstage/core-plugin-api';\n\n/**\n * @public\n * Global Floating Action Button Plugin\n */\nexport const globalFloatingActionButtonPlugin = createPlugin({\n id: 'global-floating-action-button',\n});\n\n/**\n * @public\n * Global Floating Action Button\n */\nexport const GlobalFloatingActionButton =\n globalFloatingActionButtonPlugin.provide(\n createComponentExtension({\n name: 'GlobalFloatingActionButton',\n component: {\n lazy: () =>\n import('./components/GlobalFloatingActionButton').then(\n m => m.GlobalFloatingActionButton,\n ),\n },\n }),\n );\n"],"names":[],"mappings":";;AAwBO,MAAM,mCAAmC,YAAa,CAAA;AAAA,EAC3D,EAAI,EAAA;AACN,CAAC;AAMM,MAAM,6BACX,gCAAiC,CAAA,OAAA;AAAA,EAC/B,wBAAyB,CAAA;AAAA,IACvB,IAAM,EAAA,4BAAA;AAAA,IACN,SAAW,EAAA;AAAA,MACT,IAAM,EAAA,MACJ,OAAO,gDAAyC,CAAE,CAAA,IAAA;AAAA,QAChD,OAAK,CAAE,CAAA;AAAA;AACT;AACJ,GACD;AACH;;;;"}
@@ -0,0 +1,8 @@
1
+ var Slot = /* @__PURE__ */ ((Slot2) => {
2
+ Slot2["PAGE_END"] = "page-end";
3
+ Slot2["BOTTOM_CENTER"] = "bottom-center";
4
+ return Slot2;
5
+ })(Slot || {});
6
+
7
+ export { Slot };
8
+ //# sourceMappingURL=types.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.esm.js","sources":["../src/types.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @public\n * Slot\n */\n\nexport enum Slot {\n /**\n * Positions the floating action button in the bottom-right corner of the page\n */\n PAGE_END = 'page-end',\n /**\n * Positions the floating action button at the bottom center of the page\n */\n BOTTOM_CENTER = 'bottom-center',\n}\n\n/**\n * @public\n * Floating Action Button With Positions\n */\n\nexport type FloatingActionButtonWithPositions = Array<{\n slot: Slot;\n actions: FloatingActionButton[];\n}>;\n\n/**\n * @public\n * Flex Direction\n */\n\nexport type FlexDirection = 'row' | 'row-reverse' | 'column' | 'column-reverse';\n\n/**\n * @public\n * Floating Action Button\n */\nexport type FloatingActionButton = {\n label: string;\n showLabel?: boolean;\n icon?: string | React.ReactElement;\n size?: 'small' | 'medium' | 'large';\n position?: Slot | string;\n color?:\n | 'default'\n | 'error'\n | 'info'\n | 'inherit'\n | 'primary'\n | 'secondary'\n | 'success'\n | 'warning';\n onClick?: React.MouseEventHandler;\n to?: string;\n toolTip?: string;\n priority?: number;\n visibleOnPaths?: string[];\n excludeOnPaths?: string[];\n};\n"],"names":["Slot"],"mappings":"AAqBY,IAAA,IAAA,qBAAAA,KAAL,KAAA;AAIL,EAAAA,MAAA,UAAW,CAAA,GAAA,UAAA;AAIX,EAAAA,MAAA,eAAgB,CAAA,GAAA,eAAA;AARN,EAAAA,OAAAA,KAAAA;AAAA,CAAA,EAAA,IAAA,IAAA,EAAA;;;;"}
@@ -0,0 +1,49 @@
1
+ import { Slot } from './types.esm.js';
2
+
3
+ const evaluateFloatingButtonsWithPositions = (floatingButtons) => floatingButtons.reduce(
4
+ (acc, fb) => {
5
+ const position = !fb?.position || !(fb.position in Slot) ? Slot.PAGE_END : Slot[fb.position];
6
+ const slotWithActions = acc.find((a) => a.slot === position);
7
+ if (slotWithActions) {
8
+ slotWithActions.actions.push(fb);
9
+ } else {
10
+ acc.push({
11
+ slot: position,
12
+ actions: [fb]
13
+ });
14
+ }
15
+ return acc;
16
+ },
17
+ []
18
+ );
19
+ const sortButtonsWithPriority = (floatingButtons) => {
20
+ const buttons = [...floatingButtons];
21
+ return buttons.sort((fb1, fb2) => {
22
+ if ((fb2.priority || 0) > (fb1.priority || 0)) {
23
+ return 1;
24
+ }
25
+ if ((fb2.priority || 0) < (fb1.priority || 0)) {
26
+ return -1;
27
+ }
28
+ return 0;
29
+ });
30
+ };
31
+ const filterAndSortButtons = (floatingButtons, pathname) => {
32
+ const filteredButtons = floatingButtons.filter((fb) => {
33
+ if (fb.excludeOnPaths?.includes(pathname)) {
34
+ return false;
35
+ }
36
+ if (fb.visibleOnPaths && fb.visibleOnPaths.length > 0) {
37
+ if (fb.visibleOnPaths?.includes(pathname)) {
38
+ return true;
39
+ }
40
+ return false;
41
+ }
42
+ return true;
43
+ });
44
+ const sortedButtons = sortButtonsWithPriority(filteredButtons);
45
+ return sortedButtons;
46
+ };
47
+
48
+ export { evaluateFloatingButtonsWithPositions, filterAndSortButtons, sortButtonsWithPriority };
49
+ //# sourceMappingURL=utils.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.esm.js","sources":["../src/utils.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n FloatingActionButton,\n FloatingActionButtonWithPositions,\n Slot,\n} from './types';\n\nexport const evaluateFloatingButtonsWithPositions = (\n floatingButtons: FloatingActionButton[],\n): FloatingActionButtonWithPositions =>\n floatingButtons.reduce(\n (acc: FloatingActionButtonWithPositions, fb: FloatingActionButton) => {\n const position: Slot =\n !fb?.position || !(fb.position in Slot)\n ? Slot.PAGE_END\n : Slot[fb.position as keyof typeof Slot];\n const slotWithActions = acc.find(a => a.slot === position);\n if (slotWithActions) {\n slotWithActions.actions.push(fb);\n } else {\n acc.push({\n slot: position,\n actions: [fb],\n });\n }\n return acc;\n },\n [],\n );\n\nexport const sortButtonsWithPriority = (\n floatingButtons: FloatingActionButton[],\n) => {\n const buttons = [...floatingButtons];\n return buttons.sort((fb1, fb2) => {\n if ((fb2.priority || 0) > (fb1.priority || 0)) {\n return 1;\n }\n if ((fb2.priority || 0) < (fb1.priority || 0)) {\n return -1;\n }\n return 0;\n });\n};\n\nexport const filterAndSortButtons = (\n floatingButtons: FloatingActionButton[],\n pathname: string,\n) => {\n const filteredButtons = floatingButtons.filter(fb => {\n if (fb.excludeOnPaths?.includes(pathname)) {\n return false;\n }\n if (fb.visibleOnPaths && fb.visibleOnPaths.length > 0) {\n if (fb.visibleOnPaths?.includes(pathname)) {\n return true;\n }\n return false;\n }\n return true;\n });\n const sortedButtons = sortButtonsWithPriority(filteredButtons);\n return sortedButtons;\n};\n"],"names":[],"mappings":";;AAqBa,MAAA,oCAAA,GAAuC,CAClD,eAAA,KAEA,eAAgB,CAAA,MAAA;AAAA,EACd,CAAC,KAAwC,EAA6B,KAAA;AACpE,IAAA,MAAM,QACJ,GAAA,CAAC,EAAI,EAAA,QAAA,IAAY,EAAE,EAAA,CAAG,QAAY,IAAA,IAAA,CAAA,GAC9B,IAAK,CAAA,QAAA,GACL,IAAK,CAAA,EAAA,CAAG,QAA6B,CAAA;AAC3C,IAAA,MAAM,kBAAkB,GAAI,CAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,CAAE,SAAS,QAAQ,CAAA;AACzD,IAAA,IAAI,eAAiB,EAAA;AACnB,MAAgB,eAAA,CAAA,OAAA,CAAQ,KAAK,EAAE,CAAA;AAAA,KAC1B,MAAA;AACL,MAAA,GAAA,CAAI,IAAK,CAAA;AAAA,QACP,IAAM,EAAA,QAAA;AAAA,QACN,OAAA,EAAS,CAAC,EAAE;AAAA,OACb,CAAA;AAAA;AAEH,IAAO,OAAA,GAAA;AAAA,GACT;AAAA,EACA;AACF;AAEW,MAAA,uBAAA,GAA0B,CACrC,eACG,KAAA;AACH,EAAM,MAAA,OAAA,GAAU,CAAC,GAAG,eAAe,CAAA;AACnC,EAAA,OAAO,OAAQ,CAAA,IAAA,CAAK,CAAC,GAAA,EAAK,GAAQ,KAAA;AAChC,IAAA,IAAA,CAAK,GAAI,CAAA,QAAA,IAAY,CAAM,KAAA,GAAA,CAAI,YAAY,CAAI,CAAA,EAAA;AAC7C,MAAO,OAAA,CAAA;AAAA;AAET,IAAA,IAAA,CAAK,GAAI,CAAA,QAAA,IAAY,CAAM,KAAA,GAAA,CAAI,YAAY,CAAI,CAAA,EAAA;AAC7C,MAAO,OAAA,CAAA,CAAA;AAAA;AAET,IAAO,OAAA,CAAA;AAAA,GACR,CAAA;AACH;AAEa,MAAA,oBAAA,GAAuB,CAClC,eAAA,EACA,QACG,KAAA;AACH,EAAM,MAAA,eAAA,GAAkB,eAAgB,CAAA,MAAA,CAAO,CAAM,EAAA,KAAA;AACnD,IAAA,IAAI,EAAG,CAAA,cAAA,EAAgB,QAAS,CAAA,QAAQ,CAAG,EAAA;AACzC,MAAO,OAAA,KAAA;AAAA;AAET,IAAA,IAAI,EAAG,CAAA,cAAA,IAAkB,EAAG,CAAA,cAAA,CAAe,SAAS,CAAG,EAAA;AACrD,MAAA,IAAI,EAAG,CAAA,cAAA,EAAgB,QAAS,CAAA,QAAQ,CAAG,EAAA;AACzC,QAAO,OAAA,IAAA;AAAA;AAET,MAAO,OAAA,KAAA;AAAA;AAET,IAAO,OAAA,IAAA;AAAA,GACR,CAAA;AACD,EAAM,MAAA,aAAA,GAAgB,wBAAwB,eAAe,CAAA;AAC7D,EAAO,OAAA,aAAA;AACT;;;;"}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@red-hat-developer-hub/backstage-plugin-global-floating-action-button",
3
+ "version": "0.0.1",
4
+ "main": "dist/index.esm.js",
5
+ "types": "dist/index.d.ts",
6
+ "license": "Apache-2.0",
7
+ "publishConfig": {
8
+ "access": "public",
9
+ "main": "dist/index.esm.js",
10
+ "types": "dist/index.d.ts"
11
+ },
12
+ "backstage": {
13
+ "role": "frontend-plugin",
14
+ "supported-versions": "1.32.5",
15
+ "pluginId": "global-floating-action-button",
16
+ "pluginPackages": [
17
+ "@red-hat-developer-hub/backstage-plugin-global-floating-action-button"
18
+ ]
19
+ },
20
+ "sideEffects": false,
21
+ "scripts": {
22
+ "build": "backstage-cli package build",
23
+ "clean": "backstage-cli package clean",
24
+ "lint:check": "backstage-cli package lint",
25
+ "lint:fix": "backstage-cli package lint --fix",
26
+ "postpack": "backstage-cli package postpack",
27
+ "prepack": "backstage-cli package prepack",
28
+ "start": "backstage-cli package start",
29
+ "test": "backstage-cli package test --passWithNoTests --coverage",
30
+ "tsc": "tsc",
31
+ "prettier:check": "prettier --ignore-unknown --check .",
32
+ "prettier:fix": "prettier --ignore-unknown --write .",
33
+ "ui-test": "start-server-and-test start localhost:3000 'playwright test'"
34
+ },
35
+ "dependencies": {
36
+ "@backstage/core-components": "^0.15.1",
37
+ "@backstage/core-plugin-api": "^1.10.0",
38
+ "@backstage/theme": "^0.6.0",
39
+ "@mui/icons-material": "^5.15.17",
40
+ "@mui/material": "^5.15.17",
41
+ "@mui/styles": "5.16.7",
42
+ "classnames": "^2.5.1",
43
+ "react-use": "^17.2.4"
44
+ },
45
+ "peerDependencies": {
46
+ "react": "16.13.1 || ^17.0.0 || ^18.0.0",
47
+ "react-router-dom": "^6.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "@backstage/cli": "^0.28.0",
51
+ "@backstage/dev-utils": "^1.1.2",
52
+ "@testing-library/jest-dom": "^6.0.0",
53
+ "@testing-library/react": "^14.0.0",
54
+ "react": "^16.13.1 || ^17.0.0 || ^18.0.0"
55
+ },
56
+ "files": [
57
+ "dist",
58
+ "dist-scalprum",
59
+ "app-config.yaml"
60
+ ],
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "https://github.com/redhat-developer/rhdh-plugins",
64
+ "directory": "workspaces/global-floating-action-button/plugins/global-floating-action-button"
65
+ },
66
+ "keywords": [
67
+ "lifecycle:active",
68
+ "backstage",
69
+ "plugin"
70
+ ],
71
+ "homepage": "https://red.ht/rhdh",
72
+ "bugs": "https://github.com/redhat-developer/rhdh-plugins/issues",
73
+ "maintainers": [
74
+ "@debsmita1"
75
+ ],
76
+ "author": "Red Hat",
77
+ "module": "./dist/index.esm.js"
78
+ }