@red-hat-developer-hub/backstage-plugin-dynamic-home-page 1.10.5 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +93 -1
  3. package/dist/alpha/components/CustomizableGridLayout.esm.js +60 -0
  4. package/dist/alpha/components/CustomizableGridLayout.esm.js.map +1 -0
  5. package/dist/alpha/components/HomePageLayout.esm.js +25 -0
  6. package/dist/alpha/components/HomePageLayout.esm.js.map +1 -0
  7. package/dist/alpha/components/ReadOnlyGirdLayout.esm.js +137 -0
  8. package/dist/alpha/components/ReadOnlyGirdLayout.esm.js.map +1 -0
  9. package/dist/alpha/extensions/apis.esm.js +19 -0
  10. package/dist/alpha/extensions/apis.esm.js.map +1 -0
  11. package/dist/alpha/extensions/homePageCards.esm.js +117 -0
  12. package/dist/alpha/extensions/homePageCards.esm.js.map +1 -0
  13. package/dist/alpha/extensions/homePageLayoutExtension.esm.js +57 -0
  14. package/dist/alpha/extensions/homePageLayoutExtension.esm.js.map +1 -0
  15. package/dist/alpha/utils.esm.js +6 -0
  16. package/dist/alpha/utils.esm.js.map +1 -0
  17. package/dist/alpha.d.ts +77 -0
  18. package/dist/alpha.esm.js +39 -0
  19. package/dist/alpha.esm.js.map +1 -0
  20. package/dist/components/EntitySection/EntityCard.esm.js +6 -1
  21. package/dist/components/EntitySection/EntityCard.esm.js.map +1 -1
  22. package/dist/components/EntitySection/EntitySection.esm.js +150 -100
  23. package/dist/components/EntitySection/EntitySection.esm.js.map +1 -1
  24. package/dist/components/OnboardingSection/OnboardingCard.esm.js +3 -0
  25. package/dist/components/OnboardingSection/OnboardingCard.esm.js.map +1 -1
  26. package/dist/components/OnboardingSection/OnboardingSection.esm.js +33 -17
  27. package/dist/components/OnboardingSection/OnboardingSection.esm.js.map +1 -1
  28. package/dist/components/TemplateSection/TemplateSection.esm.js +87 -47
  29. package/dist/components/TemplateSection/TemplateSection.esm.js.map +1 -1
  30. package/dist/hooks/useContainerQuery.esm.js +43 -0
  31. package/dist/hooks/useContainerQuery.esm.js.map +1 -0
  32. package/dist/index.d.ts +1 -57
  33. package/dist/index.esm.js +0 -2
  34. package/dist/index.esm.js.map +1 -1
  35. package/dist/translations/index.esm.js.map +1 -1
  36. package/dist/translations/ref.esm.js.map +1 -1
  37. package/dist/utils/GridItem.esm.js +31 -0
  38. package/dist/utils/GridItem.esm.js.map +1 -0
  39. package/package.json +35 -16
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @red-hat-developer-hub/backstage-plugin-dynamic-home-page
2
2
 
3
+ ## 1.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - abc7289: Migrate dynamic homepage to NFS
8
+
9
+ ### Patch Changes
10
+
11
+ - cf8ee79: Enhance home page layout adaptability when QuickStart is displayed.
12
+
13
+ Updated the homepage layout logic to utilize container width monitoring rather than relying on viewport-based Grid breakpoints. This change ensures the illustration card seamlessly switches to a vertical stack when the QuickStart drawer is open, independent of the user's screen resolution.
14
+
15
+ Additionally, introduced scrollbars to the onboarding, software catalog, and template sections for improved navigation and usability.
16
+
17
+ ## 1.10.6
18
+
19
+ ### Patch Changes
20
+
21
+ - b0d352a: Fixed entity cards in "Explore Your Software Catalog" section to display `metadata.title` when available, falling back to `metadata.name` only if title is not set
22
+
3
23
  ## 1.10.5
4
24
 
5
25
  ### Patch Changes
package/README.md CHANGED
@@ -2,4 +2,96 @@
2
2
 
3
3
  This is a dynamic version of the upstream [home page plugin](https://github.com/backstage/backstage/tree/master/plugins/home).
4
4
 
5
- Instead of manually adding supported "home page cards" to a custom route, it allows dynamic plugins to expose such cards.
5
+ Instead of manually adding supported "home page cards" to a custom route, it allows dynamic plugins to expose such cards. The plugin supports both the **New Frontend System (NFS)** and the **legacy** dynamic plugin model (Scalprum).
6
+
7
+ ## New Frontend System
8
+
9
+ If you're using Backstage's new frontend system, add the plugin to your app:
10
+
11
+ ```tsx
12
+ // packages/app/src/App.tsx
13
+ import { createApp } from '@backstage/frontend-defaults';
14
+ import {
15
+ homePageDevModule,
16
+ homepageTranslationsModule,
17
+ } from '@red-hat-developer-hub/backstage-plugin-dynamic-home-page/alpha';
18
+
19
+ export default createApp({
20
+ features: [
21
+ // ... other plugins (nav, signIn, etc.)
22
+ homePageDevModule,
23
+ homepageTranslationsModule,
24
+ ],
25
+ });
26
+ ```
27
+
28
+ The plugin will automatically provide:
29
+
30
+ - A homepage at `/home` (or the path configured via `page:home`)
31
+ - Default widgets: Onboarding, Entity Catalog, Templates, Quick Access, Search, Recently Visited, Top Visited, and more
32
+ - Customizable or read-only layout based on configuration, default layout being customizable
33
+
34
+ ### Configuration
35
+
36
+ Add the following to your `app-config.yaml`:
37
+
38
+ ```yaml
39
+ app:
40
+ extensions:
41
+ # Register the home page route (default: /)
42
+ - page:home:
43
+ config:
44
+ path: /
45
+ # Enable visit tracking (optional)
46
+ - api:home/visits: true
47
+ - app-root-element:home/visit-listener: true
48
+ # Configure the dynamic homepage layout
49
+ - home-page-layout:home/dynamic-homepage-layout:
50
+ config:
51
+ customizable: true # or false for read-only layout
52
+ widgetLayout:
53
+ RhdhTemplateSection:
54
+ priority: 300 # priority is considered for only Read-only Grid layout
55
+ breakpoints:
56
+ xl: { w: 12, h: 5 }
57
+ lg: { w: 12, h: 5 }
58
+ # ... md, sm, xs, xxs
59
+ RhdhEntitySection:
60
+ priority: 200
61
+ breakpoints:
62
+ xl: { w: 12, h: 7 }
63
+ # ...
64
+ RhdhOnboardingSection:
65
+ priority: 100
66
+ breakpoints:
67
+ xl: { w: 12, h: 6 }
68
+ # ...
69
+ ```
70
+
71
+ ### Modules
72
+
73
+ The following modules are available from the alpha export:
74
+
75
+ | Module | Description |
76
+ | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
77
+ | `homePageDevModule` | Home page layout and widgets (Onboarding, Entity, Templates, Quick Access, Search, Recently Visited, Top Visited, etc.) |
78
+ | `homepageTranslationsModule` | i18n translations (en, de, es, fr, it, ja) |
79
+
80
+ ### Extensions
81
+
82
+ The `homePageDevModule` extends the `home` plugin (`@backstage/plugin-home`) with:
83
+
84
+ - `home-page-layout:home/dynamic-homepage-layout` – Custom layout with config-driven widget arrangement and priority
85
+ - `home-page-widget:home/rhdh-onboarding-section` – Onboarding section
86
+ - `home-page-widget:home/rhdh-entity-section` – Software catalog section
87
+ - `home-page-widget:home/rhdh-template-section` – Templates section
88
+ - `home-page-widget:home/quick-access-card` – Quick access card
89
+ - `home-page-widget:home/search-bar` – Search bar
90
+ - `home-page-widget:home/featured-docs-card` – Featured docs
91
+ - `home-page-widget:home/recently-visited` – Recently visited
92
+ - `home-page-widget:home/top-visited` – Top visited
93
+ - `api:home/quickaccess` – Quick access API
94
+
95
+ ## Legacy System (Dynamic Plugins)
96
+
97
+ For the legacy Scalprum-based dynamic plugin model, use the main export and configure via `app-config.dynamic.yaml`. See `app-config.dynamic.yaml` in this package for the mount point configuration.
@@ -0,0 +1,60 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useMemo, Fragment as Fragment$1 } from 'react';
3
+ import { CustomHomepageGrid } from '@backstage/plugin-home';
4
+ import { useTheme } from '@mui/material/styles';
5
+ import GlobalStyles from '@mui/material/GlobalStyles';
6
+ import 'react-grid-layout/css/styles.css';
7
+ import { isCardADefaultConfiguration } from '../utils.esm.js';
8
+
9
+ const CustomizableGridLayout = ({
10
+ homepageCards
11
+ }) => {
12
+ const theme = useTheme();
13
+ const config = useMemo(() => {
14
+ const defaultConfig = [];
15
+ homepageCards.forEach((homepageCard) => {
16
+ if (!homepageCard.node) {
17
+ return;
18
+ }
19
+ if (isCardADefaultConfiguration(homepageCard)) {
20
+ const layout = homepageCard.breakpointLayouts?.xl || {};
21
+ defaultConfig.push({
22
+ component: homepageCard.component,
23
+ x: layout.x ?? 0,
24
+ y: layout.y ?? 0,
25
+ width: layout.w ?? 12,
26
+ height: layout.h ?? 4,
27
+ movable: true,
28
+ deletable: true,
29
+ resizable: true
30
+ });
31
+ }
32
+ });
33
+ return defaultConfig;
34
+ }, [homepageCards]);
35
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
36
+ /* @__PURE__ */ jsx(
37
+ GlobalStyles,
38
+ {
39
+ styles: {
40
+ '[class*="makeStyles-settingsOverlay"]': {
41
+ backgroundColor: theme.palette.mode === "dark" ? "rgba(20, 20, 20, 0.95) !important" : "rgba(40, 40, 40, 0.93) !important"
42
+ }
43
+ }
44
+ }
45
+ ),
46
+ /* @__PURE__ */ jsx(
47
+ CustomHomepageGrid,
48
+ {
49
+ config,
50
+ preventCollision: false,
51
+ compactType: "vertical",
52
+ style: { margin: "-10px" },
53
+ children: homepageCards.map((card, index) => /* @__PURE__ */ jsx(Fragment$1, { children: card.component }, card.name ?? index))
54
+ }
55
+ )
56
+ ] });
57
+ };
58
+
59
+ export { CustomizableGridLayout };
60
+ //# sourceMappingURL=CustomizableGridLayout.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomizableGridLayout.esm.js","sources":["../../../src/alpha/components/CustomizableGridLayout.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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// This complete read-only home page grid picks up the idea and styles from\n// https://github.com/backstage/backstage/blob/master/plugins/home\n// Esp. from the CustomHomepageGrid component:\n// https://github.com/backstage/backstage/blob/master/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx\n// but without the drag and drop functionality.\n\nimport { Fragment, useMemo } from 'react';\n\nimport {\n CustomHomepageGrid,\n LayoutConfiguration,\n} from '@backstage/plugin-home';\nimport { useTheme } from '@mui/material/styles';\nimport GlobalStyles from '@mui/material/GlobalStyles';\nimport { HomePageCardConfig } from '../../types';\n\nimport 'react-grid-layout/css/styles.css';\nimport { isCardADefaultConfiguration } from '../utils';\n\n/**\n * Props for the customizable grid layout.\n * @alpha\n */\nexport interface CustomizableGridLayoutProps {\n homepageCards: HomePageCardConfig[];\n}\n\n/**\n * Customizable grid layout for the NFS home page (drag, drop, resize).\n *\n * @alpha\n */\nexport const CustomizableGridLayout = ({\n homepageCards,\n}: CustomizableGridLayoutProps) => {\n const theme = useTheme();\n\n const config = useMemo(() => {\n const defaultConfig: LayoutConfiguration[] = [];\n\n homepageCards.forEach(homepageCard => {\n if (!homepageCard.node) {\n return;\n }\n\n if (isCardADefaultConfiguration(homepageCard)) {\n const layout = homepageCard.breakpointLayouts?.xl || {};\n\n defaultConfig.push({\n component: homepageCard.component,\n x: layout.x ?? 0,\n y: layout.y ?? 0,\n width: layout.w ?? 12,\n height: layout.h ?? 4,\n movable: true,\n deletable: true,\n resizable: true,\n });\n }\n });\n\n return defaultConfig;\n }, [homepageCards]);\n\n return (\n <>\n <GlobalStyles\n styles={{\n '[class*=\"makeStyles-settingsOverlay\"]': {\n backgroundColor:\n theme.palette.mode === 'dark'\n ? 'rgba(20, 20, 20, 0.95) !important'\n : 'rgba(40, 40, 40, 0.93) !important',\n },\n }}\n />\n <CustomHomepageGrid\n config={config}\n preventCollision={false}\n compactType=\"vertical\"\n style={{ margin: '-10px' }}\n >\n {homepageCards.map((card, index) => (\n <Fragment key={card.name ?? index}>{card.component}</Fragment>\n ))}\n </CustomHomepageGrid>\n </>\n );\n};\n"],"names":["Fragment"],"mappings":";;;;;;;;AAgDO,MAAM,yBAAyB,CAAC;AAAA,EACrC;AACF,CAAmC,KAAA;AACjC,EAAA,MAAM,QAAQ,QAAS,EAAA;AAEvB,EAAM,MAAA,MAAA,GAAS,QAAQ,MAAM;AAC3B,IAAA,MAAM,gBAAuC,EAAC;AAE9C,IAAA,aAAA,CAAc,QAAQ,CAAgB,YAAA,KAAA;AACpC,MAAI,IAAA,CAAC,aAAa,IAAM,EAAA;AACtB,QAAA;AAAA;AAGF,MAAI,IAAA,2BAAA,CAA4B,YAAY,CAAG,EAAA;AAC7C,QAAA,MAAM,MAAS,GAAA,YAAA,CAAa,iBAAmB,EAAA,EAAA,IAAM,EAAC;AAEtD,QAAA,aAAA,CAAc,IAAK,CAAA;AAAA,UACjB,WAAW,YAAa,CAAA,SAAA;AAAA,UACxB,CAAA,EAAG,OAAO,CAAK,IAAA,CAAA;AAAA,UACf,CAAA,EAAG,OAAO,CAAK,IAAA,CAAA;AAAA,UACf,KAAA,EAAO,OAAO,CAAK,IAAA,EAAA;AAAA,UACnB,MAAA,EAAQ,OAAO,CAAK,IAAA,CAAA;AAAA,UACpB,OAAS,EAAA,IAAA;AAAA,UACT,SAAW,EAAA,IAAA;AAAA,UACX,SAAW,EAAA;AAAA,SACZ,CAAA;AAAA;AACH,KACD,CAAA;AAED,IAAO,OAAA,aAAA;AAAA,GACT,EAAG,CAAC,aAAa,CAAC,CAAA;AAElB,EAAA,uBAEI,IAAA,CAAA,QAAA,EAAA,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACC,MAAQ,EAAA;AAAA,UACN,uCAAyC,EAAA;AAAA,YACvC,eACE,EAAA,KAAA,CAAM,OAAQ,CAAA,IAAA,KAAS,SACnB,mCACA,GAAA;AAAA;AACR;AACF;AAAA,KACF;AAAA,oBACA,GAAA;AAAA,MAAC,kBAAA;AAAA,MAAA;AAAA,QACC,MAAA;AAAA,QACA,gBAAkB,EAAA,KAAA;AAAA,QAClB,WAAY,EAAA,UAAA;AAAA,QACZ,KAAA,EAAO,EAAE,MAAA,EAAQ,OAAQ,EAAA;AAAA,QAExB,QAAc,EAAA,aAAA,CAAA,GAAA,CAAI,CAAC,IAAA,EAAM,KACxB,qBAAA,GAAA,CAACA,UAAA,EAAA,EAAmC,QAAK,EAAA,IAAA,CAAA,SAAA,EAAA,EAA1B,IAAK,CAAA,IAAA,IAAQ,KAAuB,CACpD;AAAA;AAAA;AACH,GACF,EAAA,CAAA;AAEJ;;;;"}
@@ -0,0 +1,25 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { EmptyState, Page, Content } from '@backstage/core-components';
3
+ import { useTranslation } from '../../hooks/useTranslation.esm.js';
4
+ import { Header } from '../../components/Header.esm.js';
5
+ import { ReadOnlyGridLayout } from './ReadOnlyGirdLayout.esm.js';
6
+ import { CustomizableGridLayout } from './CustomizableGridLayout.esm.js';
7
+
8
+ const HomePageLayout = ({ widgets, customizable }) => {
9
+ const { t } = useTranslation();
10
+ let content;
11
+ if (widgets.length === 0) {
12
+ content = /* @__PURE__ */ jsx(EmptyState, { title: t("homePage.empty"), missing: "content" });
13
+ } else if (customizable) {
14
+ content = /* @__PURE__ */ jsx(CustomizableGridLayout, { homepageCards: widgets });
15
+ } else {
16
+ content = /* @__PURE__ */ jsx(ReadOnlyGridLayout, { homepageCards: widgets });
17
+ }
18
+ return /* @__PURE__ */ jsxs(Page, { themeId: "home", children: [
19
+ /* @__PURE__ */ jsx(Header, { title: t("header.welcome") }),
20
+ /* @__PURE__ */ jsx(Content, { children: content })
21
+ ] });
22
+ };
23
+
24
+ export { HomePageLayout };
25
+ //# sourceMappingURL=HomePageLayout.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HomePageLayout.esm.js","sources":["../../../src/alpha/components/HomePageLayout.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Content, EmptyState, Page } from '@backstage/core-components';\nimport { useTranslation } from '../../hooks/useTranslation';\nimport { HeaderProps, Header } from '../../components/Header';\nimport { ReadOnlyGridLayout } from './ReadOnlyGirdLayout';\nimport { CustomizableGridLayout } from './CustomizableGridLayout';\nimport { HomePageCardConfig } from '../../types';\n\n/**\n * Props for the NFS home page layout component.\n * @alpha\n */\nexport interface HomePageProps extends HeaderProps {\n widgets: HomePageCardConfig[];\n customizable: boolean;\n}\n\n/**\n * NFS home page layout that renders widgets in a read-only or customizable grid.\n * Used by the dynamic-homepage-layout extension.\n *\n * @alpha\n */\nexport const HomePageLayout = ({ widgets, customizable }: HomePageProps) => {\n const { t } = useTranslation();\n\n let content: React.ReactNode;\n if (widgets.length === 0) {\n content = <EmptyState title={t('homePage.empty')} missing=\"content\" />;\n } else if (customizable) {\n content = <CustomizableGridLayout homepageCards={widgets} />;\n } else {\n content = <ReadOnlyGridLayout homepageCards={widgets} />;\n }\n\n return (\n <Page themeId=\"home\">\n <Header title={t('header.welcome')} />\n <Content>{content}</Content>\n </Page>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAsCO,MAAM,cAAiB,GAAA,CAAC,EAAE,OAAA,EAAS,cAAkC,KAAA;AAC1E,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,cAAe,EAAA;AAE7B,EAAI,IAAA,OAAA;AACJ,EAAI,IAAA,OAAA,CAAQ,WAAW,CAAG,EAAA;AACxB,IAAA,OAAA,uBAAW,UAAW,EAAA,EAAA,KAAA,EAAO,EAAE,gBAAgB,CAAA,EAAG,SAAQ,SAAU,EAAA,CAAA;AAAA,aAC3D,YAAc,EAAA;AACvB,IAAU,OAAA,mBAAA,GAAA,CAAC,sBAAuB,EAAA,EAAA,aAAA,EAAe,OAAS,EAAA,CAAA;AAAA,GACrD,MAAA;AACL,IAAU,OAAA,mBAAA,GAAA,CAAC,kBAAmB,EAAA,EAAA,aAAA,EAAe,OAAS,EAAA,CAAA;AAAA;AAGxD,EACE,uBAAA,IAAA,CAAC,IAAK,EAAA,EAAA,OAAA,EAAQ,MACZ,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,MAAO,EAAA,EAAA,KAAA,EAAO,CAAE,CAAA,gBAAgB,CAAG,EAAA,CAAA;AAAA,oBACpC,GAAA,CAAC,WAAS,QAAQ,EAAA,OAAA,EAAA;AAAA,GACpB,EAAA,CAAA;AAEJ;;;;"}
@@ -0,0 +1,137 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { Responsive } from 'react-grid-layout';
4
+ import { ErrorBoundary } from '@backstage/core-components';
5
+ import { makeStyles } from 'tss-react/mui';
6
+ import 'react-grid-layout/css/styles.css';
7
+ import useMeasure from 'react-use/lib/useMeasure';
8
+ import { isCardADefaultConfiguration } from '../utils.esm.js';
9
+
10
+ const gridGap = 16;
11
+ const defaultProps = {
12
+ // Aligned with the 1.0-1.2 home page gap.
13
+ margin: [gridGap, gridGap],
14
+ // Same as in home-plugin CustomHomepageGrid
15
+ rowHeight: 60,
16
+ // We use always a 12-column grid, but each cards can define
17
+ // their number of columns (width) and start column (x) per breakpoint.
18
+ breakpoints: {
19
+ xl: 1600,
20
+ lg: 1200,
21
+ md: 996,
22
+ sm: 768,
23
+ xs: 480,
24
+ xxs: 0
25
+ },
26
+ cols: {
27
+ xl: 12,
28
+ lg: 12,
29
+ md: 12,
30
+ sm: 12,
31
+ xs: 12,
32
+ xxs: 12
33
+ },
34
+ isDraggable: false,
35
+ isResizable: false,
36
+ compactType: null
37
+ };
38
+ const useStyles = makeStyles()({
39
+ // Make card content scrollable (so that cards don't overlap)
40
+ cardWrapper: {
41
+ '& > div[class*="MuiCard-root"]': {
42
+ width: "100%",
43
+ height: "100%"
44
+ },
45
+ '& div[class*="MuiCardContent-root"]': {
46
+ overflow: "auto"
47
+ }
48
+ }
49
+ });
50
+ const ReadOnlyGridLayout = ({
51
+ homepageCards
52
+ }) => {
53
+ const { classes } = useStyles();
54
+ const [measureRef, measureRect] = useMeasure();
55
+ const cards = useMemo(() => {
56
+ return homepageCards.filter(isCardADefaultConfiguration).map((cardData, index) => {
57
+ const id = (index + 1).toString();
58
+ const layouts2 = {};
59
+ if (cardData.breakpointLayouts) {
60
+ for (const [breakpoint, layout] of Object.entries(
61
+ cardData.breakpointLayouts
62
+ )) {
63
+ layouts2[breakpoint] = {
64
+ i: id,
65
+ x: layout.x ?? 0,
66
+ y: layout.y ?? 0,
67
+ w: layout.w ?? 12,
68
+ h: layout.h ?? 4,
69
+ isDraggable: false,
70
+ isResizable: false
71
+ };
72
+ }
73
+ } else {
74
+ ["xl", "lg", "md", "sm", "xs", "xxs"].forEach((breakpoint) => {
75
+ layouts2[breakpoint] = {
76
+ i: id,
77
+ x: 0,
78
+ y: 0,
79
+ w: 12,
80
+ h: 4,
81
+ isDraggable: false,
82
+ isResizable: false
83
+ };
84
+ });
85
+ }
86
+ const component = cardData.component;
87
+ const RenderContent = typeof component === "object" && component !== null && "Content" in component ? component.Content : component;
88
+ return {
89
+ id,
90
+ Component: RenderContent,
91
+ layouts: layouts2
92
+ };
93
+ });
94
+ }, [homepageCards]);
95
+ const layouts = useMemo(() => {
96
+ const result = {};
97
+ for (const card of cards) {
98
+ for (const [breakpoint, layoutPerBreakpoint] of Object.entries(
99
+ card.layouts
100
+ )) {
101
+ if (!result[breakpoint]) {
102
+ result[breakpoint] = [];
103
+ }
104
+ result[breakpoint].push(layoutPerBreakpoint);
105
+ }
106
+ }
107
+ return result;
108
+ }, [cards]);
109
+ const children = useMemo(() => {
110
+ return cards.map((card) => /* @__PURE__ */ jsx(
111
+ "div",
112
+ {
113
+ "data-cardid": card.id,
114
+ "data-testid": `home-page card ${card.id}`,
115
+ "data-layout": JSON.stringify(card.layouts),
116
+ className: classes.cardWrapper,
117
+ children: /* @__PURE__ */ jsx(ErrorBoundary, { children: typeof card.Component === "function" ? /* @__PURE__ */ jsx(card.Component, {}) : card.Component })
118
+ },
119
+ card.id
120
+ ));
121
+ }, [cards, classes.cardWrapper]);
122
+ return /* @__PURE__ */ jsxs("div", { style: { margin: -16 }, children: [
123
+ /* @__PURE__ */ jsx("div", { ref: measureRef }),
124
+ measureRect.width ? /* @__PURE__ */ jsx(
125
+ Responsive,
126
+ {
127
+ ...defaultProps,
128
+ width: measureRect.width,
129
+ layouts,
130
+ children
131
+ }
132
+ ) : null
133
+ ] });
134
+ };
135
+
136
+ export { ReadOnlyGridLayout };
137
+ //# sourceMappingURL=ReadOnlyGirdLayout.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReadOnlyGirdLayout.esm.js","sources":["../../../src/alpha/components/ReadOnlyGirdLayout.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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// This complete read-only home page grid picks up the idea and styles from\n// https://github.com/backstage/backstage/blob/master/plugins/home\n// Esp. from the CustomHomepageGrid component:\n// https://github.com/backstage/backstage/blob/master/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx\n// but without the drag and drop functionality.\n\nimport type { ComponentType, ReactNode } from 'react';\n\nimport { useMemo } from 'react';\nimport {\n Layout,\n Layouts,\n Responsive,\n ResponsiveProps,\n} from 'react-grid-layout';\n\nimport { ErrorBoundary } from '@backstage/core-components';\n\nimport { makeStyles } from 'tss-react/mui';\n\n// Removes the doubled scrollbar\nimport 'react-grid-layout/css/styles.css';\n\nimport useMeasure from 'react-use/lib/useMeasure';\nimport { HomePageCardConfig } from '../../types';\nimport { isCardADefaultConfiguration } from '../utils';\n\ninterface Card {\n id: string;\n Component: ComponentType<any> | ReactNode;\n layouts: Record<string, Layout>;\n}\n\nconst gridGap = 16;\n\nconst defaultProps: ResponsiveProps = {\n // Aligned with the 1.0-1.2 home page gap.\n margin: [gridGap, gridGap],\n // Same as in home-plugin CustomHomepageGrid\n rowHeight: 60,\n\n // We use always a 12-column grid, but each cards can define\n // their number of columns (width) and start column (x) per breakpoint.\n breakpoints: {\n xl: 1600,\n lg: 1200,\n md: 996,\n sm: 768,\n xs: 480,\n xxs: 0,\n },\n cols: {\n xl: 12,\n lg: 12,\n md: 12,\n sm: 12,\n xs: 12,\n xxs: 12,\n },\n\n isDraggable: false,\n isResizable: false,\n compactType: null,\n};\n\nconst useStyles = makeStyles()({\n // Make card content scrollable (so that cards don't overlap)\n cardWrapper: {\n '& > div[class*=\"MuiCard-root\"]': {\n width: '100%',\n height: '100%',\n },\n '& div[class*=\"MuiCardContent-root\"]': {\n overflow: 'auto',\n },\n },\n});\n\n/**\n * Props for the read-only grid layout.\n * @alpha\n */\nexport interface ReadOnlyGridLayoutProps {\n homepageCards: HomePageCardConfig[];\n}\n\n/**\n * Read-only grid layout for the NFS home page.\n * Respects layout configuration (breakpoints) when provided via app config.\n *\n * @alpha\n */\nexport const ReadOnlyGridLayout = ({\n homepageCards,\n}: ReadOnlyGridLayoutProps) => {\n const { classes } = useStyles();\n const [measureRef, measureRect] = useMeasure<HTMLDivElement>();\n\n const cards = useMemo<Card[]>(() => {\n return homepageCards\n .filter(isCardADefaultConfiguration)\n .map<Card>((cardData, index) => {\n const id = (index + 1).toString();\n const layouts: Record<string, Layout> = {};\n if (cardData.breakpointLayouts) {\n for (const [breakpoint, layout] of Object.entries(\n cardData.breakpointLayouts,\n )) {\n layouts[breakpoint] = {\n i: id,\n x: layout.x ?? 0,\n y: layout.y ?? 0,\n w: layout.w ?? 12,\n h: layout.h ?? 4,\n isDraggable: false,\n isResizable: false,\n };\n }\n } else {\n // Default layout for cards without a layout configuration\n ['xl', 'lg', 'md', 'sm', 'xs', 'xxs'].forEach(breakpoint => {\n layouts[breakpoint] = {\n i: id,\n x: 0,\n y: 0,\n w: 12,\n h: 4,\n isDraggable: false,\n isResizable: false,\n };\n });\n }\n\n // component can be a React element or { Content: ComponentType }; extract renderable node\n const component = cardData.component;\n const RenderContent =\n typeof component === 'object' &&\n component !== null &&\n 'Content' in component\n ? (component as { Content: ComponentType<any> }).Content\n : component;\n\n return {\n id,\n Component: RenderContent,\n layouts,\n };\n });\n }, [homepageCards]);\n\n const layouts = useMemo<Layouts>(() => {\n const result: Layouts = {};\n for (const card of cards) {\n for (const [breakpoint, layoutPerBreakpoint] of Object.entries(\n card.layouts,\n )) {\n if (!result[breakpoint]) {\n result[breakpoint] = [];\n }\n result[breakpoint].push(layoutPerBreakpoint);\n }\n }\n return result;\n }, [cards]);\n\n const children = useMemo(() => {\n return cards.map(card => (\n <div\n key={card.id}\n data-cardid={card.id}\n data-testid={`home-page card ${card.id}`}\n data-layout={JSON.stringify(card.layouts)}\n className={classes.cardWrapper}\n >\n <ErrorBoundary>\n {typeof card.Component === 'function' ? (\n <card.Component />\n ) : (\n card.Component\n )}\n </ErrorBoundary>\n </div>\n ));\n }, [cards, classes.cardWrapper]);\n\n return (\n <div style={{ margin: -gridGap }}>\n <div ref={measureRef} />\n {measureRect.width ? (\n <Responsive\n {...defaultProps}\n width={measureRect.width}\n layouts={layouts}\n >\n {children}\n </Responsive>\n ) : null}\n </div>\n );\n};\n"],"names":["layouts"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { ApiBlueprint, discoveryApiRef, configApiRef, identityApiRef } from '@backstage/frontend-plugin-api';
2
+ import { quickAccessApiRef, QuickAccessApiClient } from '../../api/QuickAccessApiClient.esm.js';
3
+
4
+ const quickAccessApi = ApiBlueprint.make({
5
+ name: "quickaccess",
6
+ disabled: false,
7
+ params: (defineParams) => defineParams({
8
+ api: quickAccessApiRef,
9
+ deps: {
10
+ discoveryApi: discoveryApiRef,
11
+ configApi: configApiRef,
12
+ identityApi: identityApiRef
13
+ },
14
+ factory: ({ discoveryApi, configApi, identityApi }) => new QuickAccessApiClient({ discoveryApi, configApi, identityApi })
15
+ })
16
+ });
17
+
18
+ export { quickAccessApi };
19
+ //# sourceMappingURL=apis.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apis.esm.js","sources":["../../../src/alpha/extensions/apis.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 ApiBlueprint,\n configApiRef,\n discoveryApiRef,\n identityApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { QuickAccessApiClient, quickAccessApiRef } from '../../api';\n\n/**\n * Quick access API for the New Frontend System.\n * Provides access to quick access links from app config.\n *\n * @alpha\n */\nconst quickAccessApi = ApiBlueprint.make({\n name: 'quickaccess',\n disabled: false,\n params: defineParams =>\n defineParams({\n api: quickAccessApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n configApi: configApiRef,\n identityApi: identityApiRef,\n },\n factory: ({ discoveryApi, configApi, identityApi }) =>\n new QuickAccessApiClient({ discoveryApi, configApi, identityApi }),\n }),\n});\n\nexport { quickAccessApi };\n"],"names":[],"mappings":";;;AA8BM,MAAA,cAAA,GAAiB,aAAa,IAAK,CAAA;AAAA,EACvC,IAAM,EAAA,aAAA;AAAA,EACN,QAAU,EAAA,KAAA;AAAA,EACV,MAAA,EAAQ,kBACN,YAAa,CAAA;AAAA,IACX,GAAK,EAAA,iBAAA;AAAA,IACL,IAAM,EAAA;AAAA,MACJ,YAAc,EAAA,eAAA;AAAA,MACd,SAAW,EAAA,YAAA;AAAA,MACX,WAAa,EAAA;AAAA,KACf;AAAA,IACA,OAAS,EAAA,CAAC,EAAE,YAAA,EAAc,SAAW,EAAA,WAAA,EACnC,KAAA,IAAI,oBAAqB,CAAA,EAAE,YAAc,EAAA,SAAA,EAAW,aAAa;AAAA,GACpE;AACL,CAAC;;;;"}
@@ -0,0 +1,117 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { HomePageWidgetBlueprint } from '@backstage/plugin-home-react/alpha';
3
+ import homePlugin from '@backstage/plugin-home/alpha';
4
+ import { compatWrapper } from '@backstage/core-compat-api';
5
+
6
+ const defaultCardLayout = {
7
+ width: {
8
+ minColumns: 4,
9
+ maxColumns: 12,
10
+ defaultColumns: 12
11
+ },
12
+ height: {
13
+ minRows: 2,
14
+ maxRows: 12,
15
+ defaultRows: 4
16
+ }
17
+ };
18
+ const onboardingSectionWidget = HomePageWidgetBlueprint.make({
19
+ name: "rhdh-onboarding-section",
20
+ params: {
21
+ name: "RhdhOnboardingSection",
22
+ layout: defaultCardLayout,
23
+ components: () => import('../../components/OnboardingSection/index.esm.js').then((m) => ({
24
+ Content: m.OnboardingSection
25
+ }))
26
+ }
27
+ });
28
+ const entitySectionWidget = HomePageWidgetBlueprint.make({
29
+ name: "rhdh-entity-section",
30
+ params: {
31
+ name: "RhdhEntitySection",
32
+ layout: defaultCardLayout,
33
+ components: () => import('../../components/EntitySection/index.esm.js').then((m) => ({
34
+ Content: () => compatWrapper(/* @__PURE__ */ jsx(m.EntitySection, {}))
35
+ }))
36
+ }
37
+ });
38
+ const templateSectionWidget = HomePageWidgetBlueprint.make({
39
+ name: "rhdh-template-section",
40
+ params: {
41
+ name: "RhdhTemplateSection",
42
+ layout: defaultCardLayout,
43
+ components: () => import('../../components/TemplateSection/index.esm.js').then((m) => ({
44
+ Content: m.TemplateSection
45
+ }))
46
+ }
47
+ });
48
+ const quickAccessCardWidget = HomePageWidgetBlueprint.make({
49
+ name: "quick-access-card",
50
+ params: {
51
+ name: "QuickAccessCard",
52
+ layout: defaultCardLayout,
53
+ components: () => import('../../components/QuickAccessCard.esm.js').then((m) => ({
54
+ Content: () => compatWrapper(/* @__PURE__ */ jsx(m.QuickAccessCard, {}))
55
+ }))
56
+ }
57
+ });
58
+ const searchBarWidget = HomePageWidgetBlueprint.make({
59
+ name: "search-bar",
60
+ params: {
61
+ name: "SearchBar",
62
+ layout: {
63
+ ...defaultCardLayout,
64
+ height: {
65
+ ...defaultCardLayout.height,
66
+ defaultRows: 2,
67
+ minRows: 1,
68
+ maxRows: 1
69
+ }
70
+ },
71
+ components: () => import('../../components/SearchBar.esm.js').then((m) => ({
72
+ Content: () => compatWrapper(/* @__PURE__ */ jsx(m.SearchBar, {}))
73
+ }))
74
+ }
75
+ });
76
+ const featuredDocsCardWidget = HomePageWidgetBlueprint.make({
77
+ name: "featured-docs-card",
78
+ params: {
79
+ name: "FeaturedDocsCard",
80
+ layout: defaultCardLayout,
81
+ components: () => import('../../components/FeaturedDocsCard.esm.js').then((m) => ({
82
+ Content: m.FeaturedDocsCard
83
+ }))
84
+ }
85
+ });
86
+ const catalogStarredWidget = homePlugin.getExtension("home-page-widget:home/starred-entities").override({
87
+ params: {
88
+ name: "CatalogStarred",
89
+ title: "Starred catalog entities"
90
+ }
91
+ });
92
+ const disableToolkit = homePlugin.getExtension("home-page-widget:home/toolkit").override({
93
+ disabled: true
94
+ });
95
+ const RecentlyVisitedWidget = HomePageWidgetBlueprint.make({
96
+ name: "recently-visited",
97
+ params: {
98
+ layout: defaultCardLayout,
99
+ name: "Recently visited",
100
+ components: () => import('@backstage/plugin-home').then((m) => ({
101
+ Content: m.HomePageRecentlyVisited
102
+ }))
103
+ }
104
+ });
105
+ const TopVisitedWidget = HomePageWidgetBlueprint.make({
106
+ name: "top-visited",
107
+ params: {
108
+ layout: defaultCardLayout,
109
+ name: "Top visited",
110
+ components: () => import('@backstage/plugin-home').then((m) => ({
111
+ Content: () => /* @__PURE__ */ jsx(m.HomePageTopVisited, {})
112
+ }))
113
+ }
114
+ });
115
+
116
+ export { RecentlyVisitedWidget, TopVisitedWidget, catalogStarredWidget, disableToolkit, entitySectionWidget, featuredDocsCardWidget, onboardingSectionWidget, quickAccessCardWidget, searchBarWidget, templateSectionWidget };
117
+ //# sourceMappingURL=homePageCards.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homePageCards.esm.js","sources":["../../../src/alpha/extensions/homePageCards.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { HomePageWidgetBlueprint } from '@backstage/plugin-home-react/alpha';\nimport homePlugin from '@backstage/plugin-home/alpha';\nimport { compatWrapper } from '@backstage/core-compat-api';\n\nconst defaultCardLayout = {\n width: {\n minColumns: 4,\n maxColumns: 12,\n defaultColumns: 12,\n },\n height: {\n minRows: 2,\n maxRows: 12,\n defaultRows: 4,\n },\n} as const;\n\n/**\n * NFS widget: OnboardingSection (migrated from mountPoint home.page/cards).\n * @alpha\n */\nexport const onboardingSectionWidget = HomePageWidgetBlueprint.make({\n name: 'rhdh-onboarding-section',\n params: {\n name: 'RhdhOnboardingSection',\n layout: defaultCardLayout,\n components: () =>\n import('../../components/OnboardingSection').then(m => ({\n Content: m.OnboardingSection,\n })),\n },\n});\n\n/**\n * NFS widget: EntitySection (migrated from mountPoint home.page/cards).\n * @alpha\n */\nexport const entitySectionWidget = HomePageWidgetBlueprint.make({\n name: 'rhdh-entity-section',\n params: {\n name: 'RhdhEntitySection',\n layout: defaultCardLayout,\n components: () =>\n import('../../components/EntitySection').then(m => ({\n Content: () => compatWrapper(<m.EntitySection />),\n })),\n },\n});\n\n/**\n * NFS widget: TemplateSection (migrated from mountPoint home.page/cards).\n * @alpha\n */\nexport const templateSectionWidget = HomePageWidgetBlueprint.make({\n name: 'rhdh-template-section',\n params: {\n name: 'RhdhTemplateSection',\n layout: defaultCardLayout,\n components: () =>\n import('../../components/TemplateSection').then(m => ({\n Content: m.TemplateSection,\n })),\n },\n});\n\n/**\n * NFS widget: QuickAccessCard (migrated from mountPoint home.page/cards).\n * @alpha\n */\nexport const quickAccessCardWidget = HomePageWidgetBlueprint.make({\n name: 'quick-access-card',\n params: {\n name: 'QuickAccessCard',\n layout: defaultCardLayout,\n components: () =>\n import('../../components/QuickAccessCard').then(m => ({\n Content: () => compatWrapper(<m.QuickAccessCard />),\n })),\n },\n});\n\n/**\n * NFS widget: SearchBar (migrated from mountPoint home.page/cards).\n * @alpha\n */\nexport const searchBarWidget = HomePageWidgetBlueprint.make({\n name: 'search-bar',\n params: {\n name: 'SearchBar',\n layout: {\n ...defaultCardLayout,\n height: {\n ...defaultCardLayout.height,\n defaultRows: 2,\n minRows: 1,\n maxRows: 1,\n },\n },\n components: () =>\n import('../../components/SearchBar').then(m => ({\n Content: () => compatWrapper(<m.SearchBar />),\n })),\n },\n});\n\n/**\n * NFS widget: FeaturedDocsCard (migrated from mountPoint home.page/cards).\n * @alpha\n */\nexport const featuredDocsCardWidget = HomePageWidgetBlueprint.make({\n name: 'featured-docs-card',\n params: {\n name: 'FeaturedDocsCard',\n layout: defaultCardLayout,\n components: () =>\n import('../../components/FeaturedDocsCard').then(m => ({\n Content: m.FeaturedDocsCard,\n })),\n },\n});\n\n/**\n * NFS widget: CatalogStarred (migrated from mountPoint home.page/cards).\n * @alpha\n */\nexport const catalogStarredWidget = homePlugin\n .getExtension('home-page-widget:home/starred-entities')\n .override({\n params: {\n name: 'CatalogStarred',\n title: 'Starred catalog entities',\n },\n });\n\n/**\n * Disables the default home plugin toolkit widget.\n * @alpha\n */\nexport const disableToolkit = homePlugin\n .getExtension('home-page-widget:home/toolkit')\n .override({\n disabled: true,\n });\n\n/**\n * NFS widget: RecentlyVisited (migrated from mountPoint home.page/cards).\n * @alpha\n */\nexport const RecentlyVisitedWidget = HomePageWidgetBlueprint.make({\n name: 'recently-visited',\n params: {\n layout: defaultCardLayout,\n name: 'Recently visited',\n components: () =>\n import('@backstage/plugin-home').then(m => ({\n Content: m.HomePageRecentlyVisited,\n })),\n },\n});\n\n/**\n * NFS widget: TopVisited (migrated from mountPoint home.page/cards).\n * @alpha\n */\nexport const TopVisitedWidget = HomePageWidgetBlueprint.make({\n name: 'top-visited',\n params: {\n layout: defaultCardLayout,\n name: 'Top visited',\n components: () =>\n import('@backstage/plugin-home').then(m => ({\n Content: () => <m.HomePageTopVisited />,\n })),\n },\n});\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { HomePageLayoutBlueprint } from '@backstage/plugin-home-react/alpha';
3
+ import { HomePageLayout } from '../components/HomePageLayout.esm.js';
4
+
5
+ const homePageLayoutExtension = HomePageLayoutBlueprint.makeWithOverrides({
6
+ name: "dynamic-homepage-layout",
7
+ config: {
8
+ schema: {
9
+ customizable: (z) => z.boolean().optional(),
10
+ widgetLayout: (z) => z.record(
11
+ z.object({
12
+ priority: z.number().optional(),
13
+ breakpoints: z.record(
14
+ z.object({
15
+ w: z.number().optional(),
16
+ h: z.number().optional(),
17
+ x: z.number().optional(),
18
+ y: z.number().optional()
19
+ })
20
+ ).optional()
21
+ })
22
+ ).optional()
23
+ }
24
+ },
25
+ factory(originalFactory, { config }) {
26
+ const customizable = config.customizable ?? true;
27
+ const layoutConfig = config.widgetLayout ?? {};
28
+ return originalFactory({
29
+ loader: async () => function CustomHomePageLayout({ widgets }) {
30
+ const processedWidgets = widgets.map((widget) => {
31
+ const widgetConfig = layoutConfig[widget.name ?? ""];
32
+ const configBreakpoints = widgetConfig?.breakpoints;
33
+ if (!configBreakpoints) return widget;
34
+ return {
35
+ ...widget,
36
+ breakpointLayouts: configBreakpoints
37
+ };
38
+ }).sort((a, b) => {
39
+ if (customizable) return 0;
40
+ const priorityA = layoutConfig[a.name ?? ""]?.priority ?? 0;
41
+ const priorityB = layoutConfig[b.name ?? ""]?.priority ?? 0;
42
+ return priorityB - priorityA;
43
+ });
44
+ return /* @__PURE__ */ jsx(
45
+ HomePageLayout,
46
+ {
47
+ widgets: processedWidgets,
48
+ customizable
49
+ }
50
+ );
51
+ }
52
+ });
53
+ }
54
+ });
55
+
56
+ export { homePageLayoutExtension };
57
+ //# sourceMappingURL=homePageLayoutExtension.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homePageLayoutExtension.esm.js","sources":["../../../src/alpha/extensions/homePageLayoutExtension.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { HomePageLayoutBlueprint } from '@backstage/plugin-home-react/alpha';\nimport { HomePageLayout } from '../components/HomePageLayout';\nimport { HomePageCardConfig } from '../../types';\n\n/**\n * Custom home page layout extension for the New Frontend System.\n *\n * Config-driven layout with `widgetLayout` (priority, breakpoints per widget),\n * supports both customizable (drag/drop) and read-only modes.\n *\n * @alpha\n */\nexport const homePageLayoutExtension =\n HomePageLayoutBlueprint.makeWithOverrides({\n name: 'dynamic-homepage-layout',\n config: {\n schema: {\n customizable: z => z.boolean().optional(),\n widgetLayout: z =>\n z\n .record(\n z.object({\n priority: z.number().optional(),\n breakpoints: z\n .record(\n z.object({\n w: z.number().optional(),\n h: z.number().optional(),\n x: z.number().optional(),\n y: z.number().optional(),\n }),\n )\n .optional(),\n }),\n )\n .optional(),\n },\n },\n factory(originalFactory, { config }) {\n const customizable = config.customizable ?? true;\n const layoutConfig = config.widgetLayout ?? {};\n\n return originalFactory({\n loader: async () =>\n function CustomHomePageLayout({ widgets }) {\n const processedWidgets: HomePageCardConfig[] = widgets\n .map(widget => {\n const widgetConfig = layoutConfig[widget.name ?? ''];\n const configBreakpoints = widgetConfig?.breakpoints;\n\n if (!configBreakpoints) return widget;\n\n return {\n ...widget,\n breakpointLayouts: configBreakpoints,\n };\n })\n .sort((a, b) => {\n if (customizable) return 0; // keep original order\n\n const priorityA = layoutConfig[a.name ?? '']?.priority ?? 0;\n const priorityB = layoutConfig[b.name ?? '']?.priority ?? 0;\n return priorityB - priorityA;\n });\n\n return (\n <HomePageLayout\n widgets={processedWidgets}\n customizable={customizable}\n />\n );\n },\n });\n },\n });\n"],"names":[],"mappings":";;;;AA4Ba,MAAA,uBAAA,GACX,wBAAwB,iBAAkB,CAAA;AAAA,EACxC,IAAM,EAAA,yBAAA;AAAA,EACN,MAAQ,EAAA;AAAA,IACN,MAAQ,EAAA;AAAA,MACN,YAAc,EAAA,CAAA,CAAA,KAAK,CAAE,CAAA,OAAA,GAAU,QAAS,EAAA;AAAA,MACxC,YAAA,EAAc,OACZ,CACG,CAAA,MAAA;AAAA,QACC,EAAE,MAAO,CAAA;AAAA,UACP,QAAU,EAAA,CAAA,CAAE,MAAO,EAAA,CAAE,QAAS,EAAA;AAAA,UAC9B,aAAa,CACV,CAAA,MAAA;AAAA,YACC,EAAE,MAAO,CAAA;AAAA,cACP,CAAG,EAAA,CAAA,CAAE,MAAO,EAAA,CAAE,QAAS,EAAA;AAAA,cACvB,CAAG,EAAA,CAAA,CAAE,MAAO,EAAA,CAAE,QAAS,EAAA;AAAA,cACvB,CAAG,EAAA,CAAA,CAAE,MAAO,EAAA,CAAE,QAAS,EAAA;AAAA,cACvB,CAAG,EAAA,CAAA,CAAE,MAAO,EAAA,CAAE,QAAS;AAAA,aACxB;AAAA,YAEF,QAAS;AAAA,SACb;AAAA,QAEF,QAAS;AAAA;AAChB,GACF;AAAA,EACA,OAAQ,CAAA,eAAA,EAAiB,EAAE,MAAA,EAAU,EAAA;AACnC,IAAM,MAAA,YAAA,GAAe,OAAO,YAAgB,IAAA,IAAA;AAC5C,IAAM,MAAA,YAAA,GAAe,MAAO,CAAA,YAAA,IAAgB,EAAC;AAE7C,IAAA,OAAO,eAAgB,CAAA;AAAA,MACrB,QAAQ,YACN,SAAS,oBAAqB,CAAA,EAAE,SAAW,EAAA;AACzC,QAAM,MAAA,gBAAA,GAAyC,OAC5C,CAAA,GAAA,CAAI,CAAU,MAAA,KAAA;AACb,UAAA,MAAM,YAAe,GAAA,YAAA,CAAa,MAAO,CAAA,IAAA,IAAQ,EAAE,CAAA;AACnD,UAAA,MAAM,oBAAoB,YAAc,EAAA,WAAA;AAExC,UAAI,IAAA,CAAC,mBAA0B,OAAA,MAAA;AAE/B,UAAO,OAAA;AAAA,YACL,GAAG,MAAA;AAAA,YACH,iBAAmB,EAAA;AAAA,WACrB;AAAA,SACD,CAAA,CACA,IAAK,CAAA,CAAC,GAAG,CAAM,KAAA;AACd,UAAA,IAAI,cAAqB,OAAA,CAAA;AAEzB,UAAA,MAAM,YAAY,YAAa,CAAA,CAAA,CAAE,IAAQ,IAAA,EAAE,GAAG,QAAY,IAAA,CAAA;AAC1D,UAAA,MAAM,YAAY,YAAa,CAAA,CAAA,CAAE,IAAQ,IAAA,EAAE,GAAG,QAAY,IAAA,CAAA;AAC1D,UAAA,OAAO,SAAY,GAAA,SAAA;AAAA,SACpB,CAAA;AAEH,QACE,uBAAA,GAAA;AAAA,UAAC,cAAA;AAAA,UAAA;AAAA,YACC,OAAS,EAAA,gBAAA;AAAA,YACT;AAAA;AAAA,SACF;AAAA;AAEJ,KACH,CAAA;AAAA;AAEL,CAAC;;;;"}
@@ -0,0 +1,6 @@
1
+ function isCardADefaultConfiguration(cardData) {
2
+ return !!cardData.breakpointLayouts || Object.entries(cardData.breakpointLayouts ?? {}).length === 0;
3
+ }
4
+
5
+ export { isCardADefaultConfiguration };
6
+ //# sourceMappingURL=utils.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.esm.js","sources":["../../src/alpha/utils.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { HomePageCardConfig } from '../types';\n\nexport function isCardADefaultConfiguration(\n cardData: HomePageCardConfig,\n): boolean {\n return (\n !!cardData.breakpointLayouts ||\n Object.entries(cardData.breakpointLayouts ?? {}).length === 0\n );\n}\n"],"names":[],"mappings":"AAkBO,SAAS,4BACd,QACS,EAAA;AACT,EACE,OAAA,CAAC,CAAC,QAAA,CAAS,iBACX,IAAA,MAAA,CAAO,OAAQ,CAAA,QAAA,CAAS,iBAAqB,IAAA,EAAE,CAAA,CAAE,MAAW,KAAA,CAAA;AAEhE;;;;"}