@red-hat-developer-hub/backstage-plugin-dynamic-home-page 1.11.0 → 1.13.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 (45) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/alpha/components/CustomizableGridLayout.esm.js +17 -7
  3. package/dist/alpha/components/CustomizableGridLayout.esm.js.map +1 -1
  4. package/dist/alpha/extensions/apis.esm.js +15 -2
  5. package/dist/alpha/extensions/apis.esm.js.map +1 -1
  6. package/dist/alpha/extensions/homePageCards.esm.js +6 -6
  7. package/dist/alpha/extensions/homePageCards.esm.js.map +1 -1
  8. package/dist/alpha.d.ts +1 -1
  9. package/dist/alpha.esm.js +2 -1
  10. package/dist/alpha.esm.js.map +1 -1
  11. package/dist/api/DefaultWidgetsApiClient.esm.js +26 -0
  12. package/dist/api/DefaultWidgetsApiClient.esm.js.map +1 -0
  13. package/dist/components/CustomizableGrid.esm.js +17 -7
  14. package/dist/components/CustomizableGrid.esm.js.map +1 -1
  15. package/dist/components/DefaultWidgetsCustomizableGrid.esm.js +138 -0
  16. package/dist/components/DefaultWidgetsCustomizableGrid.esm.js.map +1 -0
  17. package/dist/components/DefaultWidgetsReadOnlyGrid.esm.js +146 -0
  18. package/dist/components/DefaultWidgetsReadOnlyGrid.esm.js.map +1 -0
  19. package/dist/components/EntitySection/EntitySection.esm.js +1 -8
  20. package/dist/components/EntitySection/EntitySection.esm.js.map +1 -1
  21. package/dist/components/HomePage.esm.js +27 -3
  22. package/dist/components/HomePage.esm.js.map +1 -1
  23. package/dist/hooks/useContainerQuery.esm.js +40 -5
  24. package/dist/hooks/useContainerQuery.esm.js.map +1 -1
  25. package/dist/hooks/useDefaultWidgets.esm.js +19 -0
  26. package/dist/hooks/useDefaultWidgets.esm.js.map +1 -0
  27. package/dist/index.d.ts +24 -11
  28. package/dist/index.esm.js +1 -0
  29. package/dist/index.esm.js.map +1 -1
  30. package/dist/plugin.esm.js +11 -2
  31. package/dist/plugin.esm.js.map +1 -1
  32. package/dist/translations/de.esm.js +1 -1
  33. package/dist/translations/de.esm.js.map +1 -1
  34. package/dist/translations/es.esm.js +1 -1
  35. package/dist/translations/es.esm.js.map +1 -1
  36. package/dist/translations/fr.esm.js +1 -1
  37. package/dist/translations/fr.esm.js.map +1 -1
  38. package/dist/translations/it.esm.js +1 -1
  39. package/dist/translations/it.esm.js.map +1 -1
  40. package/dist/translations/ja.esm.js +1 -1
  41. package/dist/translations/ja.esm.js.map +1 -1
  42. package/dist/translations/ref.esm.js +1 -1
  43. package/dist/translations/ref.esm.js.map +1 -1
  44. package/dist/utils/customizable-cards.esm.js.map +1 -1
  45. package/package.json +27 -23
@@ -0,0 +1,146 @@
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
+
9
+ const gridGap = 16;
10
+ const defaultProps = {
11
+ margin: [gridGap, gridGap],
12
+ rowHeight: 60,
13
+ breakpoints: {
14
+ xl: 1600,
15
+ lg: 1200,
16
+ md: 996,
17
+ sm: 768,
18
+ xs: 480,
19
+ xxs: 0
20
+ },
21
+ cols: {
22
+ xl: 12,
23
+ lg: 12,
24
+ md: 12,
25
+ sm: 12,
26
+ xs: 12,
27
+ xxs: 12
28
+ },
29
+ isDraggable: false,
30
+ isResizable: false,
31
+ compactType: null
32
+ };
33
+ const useStyles = makeStyles()({
34
+ cardWrapper: {
35
+ '& > div[class*="MuiCard-root"]': {
36
+ width: "100%",
37
+ height: "100%"
38
+ },
39
+ '& div[class*="MuiCardContent-root"]': {
40
+ overflow: "auto"
41
+ }
42
+ }
43
+ });
44
+ const DefaultWidgetsReadOnlyGrid = ({
45
+ defaultWidgets,
46
+ mountPoints
47
+ }) => {
48
+ const { classes } = useStyles();
49
+ const [measureRef, measureRect] = useMeasure();
50
+ const mountPointsByRef = useMemo(() => {
51
+ const map = /* @__PURE__ */ new Map();
52
+ for (const mp of mountPoints) {
53
+ if (mp.config?.id) {
54
+ map.set(mp.config.id, mp);
55
+ }
56
+ }
57
+ return map;
58
+ }, [mountPoints]);
59
+ const cards = useMemo(() => {
60
+ return defaultWidgets.map((widget, index) => {
61
+ const mountPoint = mountPointsByRef.get(widget.ref);
62
+ if (!mountPoint) {
63
+ console.warn(
64
+ `No mount point found for widget with ref ${widget.ref}. Available mount points: ${[...mountPointsByRef.keys()].join(", ")}`
65
+ );
66
+ return null;
67
+ }
68
+ const id = (index + 1).toString();
69
+ const layouts2 = {};
70
+ const widgetLayout = widget.layout;
71
+ if (widgetLayout) {
72
+ for (const [breakpoint, layout] of Object.entries(widgetLayout)) {
73
+ layouts2[breakpoint] = {
74
+ i: id,
75
+ x: layout.x ?? 0,
76
+ y: layout.y ?? 0,
77
+ w: layout.w ?? 12,
78
+ h: layout.h ?? 4,
79
+ isDraggable: false,
80
+ isResizable: false
81
+ };
82
+ }
83
+ } else {
84
+ ["xl", "lg", "md", "sm", "xs", "xxs"].forEach((breakpoint) => {
85
+ layouts2[breakpoint] = {
86
+ i: id,
87
+ x: 0,
88
+ y: 0,
89
+ w: 12,
90
+ h: 4,
91
+ isDraggable: false,
92
+ isResizable: false
93
+ };
94
+ });
95
+ }
96
+ return {
97
+ id,
98
+ Component: mountPoint.Component,
99
+ props: widget.props,
100
+ layouts: layouts2
101
+ };
102
+ }).filter((card) => card !== null);
103
+ }, [defaultWidgets, mountPointsByRef]);
104
+ const layouts = useMemo(() => {
105
+ const result = {};
106
+ for (const card of cards) {
107
+ for (const [breakpoint, layoutPerBreakpoint] of Object.entries(
108
+ card.layouts
109
+ )) {
110
+ if (!result[breakpoint]) {
111
+ result[breakpoint] = [];
112
+ }
113
+ result[breakpoint].push(layoutPerBreakpoint);
114
+ }
115
+ }
116
+ return result;
117
+ }, [cards]);
118
+ const children = useMemo(() => {
119
+ return cards.map((card) => /* @__PURE__ */ jsx(
120
+ "div",
121
+ {
122
+ "data-cardid": card.id,
123
+ "data-testid": `home-page card ${card.id}`,
124
+ "data-layout": JSON.stringify(card.layouts),
125
+ className: classes.cardWrapper,
126
+ children: /* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(card.Component, { ...card.props }) })
127
+ },
128
+ card.id
129
+ ));
130
+ }, [cards, classes.cardWrapper]);
131
+ return /* @__PURE__ */ jsxs("div", { style: { margin: -16 }, children: [
132
+ /* @__PURE__ */ jsx("div", { ref: measureRef }),
133
+ measureRect.width ? /* @__PURE__ */ jsx(
134
+ Responsive,
135
+ {
136
+ ...defaultProps,
137
+ width: measureRect.width,
138
+ layouts,
139
+ children
140
+ }
141
+ ) : null
142
+ ] });
143
+ };
144
+
145
+ export { DefaultWidgetsReadOnlyGrid };
146
+ //# sourceMappingURL=DefaultWidgetsReadOnlyGrid.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultWidgetsReadOnlyGrid.esm.js","sources":["../../src/components/DefaultWidgetsReadOnlyGrid.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 type { ComponentType } 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\nimport 'react-grid-layout/css/styles.css';\n\nimport useMeasure from 'react-use/lib/useMeasure';\n\nimport type { VisibleDefaultWidget } from '../api/DefaultWidgetsApiClient';\nimport { HomePageCardMountPoint } from '../types';\n\ninterface Card {\n id: string;\n Component: ComponentType<any>;\n props?: Record<string, unknown>;\n layouts: Record<string, Layout>;\n}\n\nconst gridGap = 16;\n\nconst defaultProps: ResponsiveProps = {\n margin: [gridGap, gridGap],\n rowHeight: 60,\n\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 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\nexport interface DefaultWidgetsReadOnlyGridProps {\n defaultWidgets: VisibleDefaultWidget[];\n mountPoints: HomePageCardMountPoint[];\n}\n\nexport const DefaultWidgetsReadOnlyGrid = ({\n defaultWidgets,\n mountPoints,\n}: DefaultWidgetsReadOnlyGridProps) => {\n const { classes } = useStyles();\n const [measureRef, measureRect] = useMeasure<HTMLDivElement>();\n\n const mountPointsByRef = useMemo(() => {\n const map = new Map<string, HomePageCardMountPoint>();\n for (const mp of mountPoints) {\n if (mp.config?.id) {\n map.set(mp.config.id, mp);\n }\n }\n return map;\n }, [mountPoints]);\n\n const cards = useMemo<Card[]>(() => {\n return defaultWidgets\n .map<Card | null>((widget, index) => {\n const mountPoint = mountPointsByRef.get(widget.ref);\n if (!mountPoint) {\n // eslint-disable-next-line no-console\n console.warn(\n `No mount point found for widget with ref ${widget.ref}. Available mount points: ${[...mountPointsByRef.keys()].join(', ')}`,\n );\n return null;\n }\n\n const id = (index + 1).toString();\n const layouts: Record<string, Layout> = {};\n const widgetLayout = widget.layout as\n | Record<string, { x?: number; y?: number; w?: number; h?: number }>\n | undefined;\n\n if (widgetLayout) {\n for (const [breakpoint, layout] of Object.entries(widgetLayout)) {\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 ['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 return {\n id,\n Component: mountPoint.Component,\n props: widget.props,\n layouts,\n };\n })\n .filter((card): card is Card => card !== null);\n }, [defaultWidgets, mountPointsByRef]);\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 <card.Component {...card.props} />\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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"}
@@ -18,7 +18,6 @@ import HomePageEntityIllustration from '../../images/homepage-entities-1.svg';
18
18
  import { useEntities } from '../../hooks/useEntities.esm.js';
19
19
  import { hasEntityIllustrationUserDismissed, addDismissedEntityIllustrationUsers } from '../../utils/utils.esm.js';
20
20
  import { useTranslation } from '../../hooks/useTranslation.esm.js';
21
- import { Trans } from '../Trans.esm.js';
22
21
  import { containerGridItemSx } from '../../utils/GridItem.esm.js';
23
22
  import { useContainerQuery } from '../../hooks/useContainerQuery.esm.js';
24
23
 
@@ -268,13 +267,7 @@ const EntitySection = () => {
268
267
  },
269
268
  children: [
270
269
  content,
271
- entities?.length > 0 && /* @__PURE__ */ jsx(Box, { sx: { pt: 2 }, children: /* @__PURE__ */ jsx(ViewMoreLink, { to: "/catalog", children: /* @__PURE__ */ jsx(
272
- Trans,
273
- {
274
- message: "entities.viewAll",
275
- params: { count: data?.totalItems?.toString() || "" }
276
- }
277
- ) }) })
270
+ entities?.length > 0 && /* @__PURE__ */ jsx(Box, { sx: { pt: 2 }, children: /* @__PURE__ */ jsx(ViewMoreLink, { to: "/catalog", children: t("entities.browseTheCatalog") }) })
278
271
  ]
279
272
  }
280
273
  )
@@ -1 +1 @@
1
- {"version":3,"file":"EntitySection.esm.js","sources":["../../../src/components/EntitySection/EntitySection.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 */\nimport type { ReactNode } from 'react';\n\nimport { useState, useEffect, Fragment, useRef, useCallback } from 'react';\n\nimport {\n CodeSnippet,\n WarningPanel,\n Link as BackstageLink,\n} from '@backstage/core-components';\nimport { useUserProfile } from '@backstage/plugin-user-settings';\n\nimport Grid from '@mui/material/Grid';\nimport Box from '@mui/material/Box';\nimport Card from '@mui/material/Card';\nimport IconButton from '@mui/material/IconButton';\nimport Skeleton from '@mui/material/Skeleton';\nimport Typography from '@mui/material/Typography';\nimport CloseIcon from '@mui/icons-material/Close';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport CardContent from '@mui/material/CardContent';\nimport { useTheme, styled } from '@mui/material/styles';\n\nimport EntityCard from './EntityCard';\nimport { ViewMoreLink } from './ViewMoreLink';\nimport HomePageEntityIllustration from '../../images/homepage-entities-1.svg';\nimport { useEntities } from '../../hooks/useEntities';\nimport {\n addDismissedEntityIllustrationUsers,\n hasEntityIllustrationUserDismissed,\n} from '../../utils/utils';\nimport { useTranslation } from '../../hooks/useTranslation';\nimport { Trans } from '../Trans';\nimport { containerGridItemSx } from '../../utils/GridItem';\nimport {\n useContainerQuery,\n type ContainerSize,\n} from '../../hooks/useContainerQuery';\n\nconst StyledLink = styled(BackstageLink)(({ theme }) => ({\n textDecoration: 'none',\n padding: theme.spacing(1, 1.5),\n fontSize: '16px',\n display: 'inline-flex',\n border: `1px solid ${theme.palette.primary.main}`,\n borderRadius: 4,\n}));\n\nexport const EntitySection = () => {\n const theme = useTheme();\n const { t } = useTranslation();\n const { displayName, loading: profileLoading } = useUserProfile();\n const [isRemoveFirstCard, setIsRemoveFirstCard] = useState(false);\n const [showDiscoveryCard, setShowDiscoveryCard] = useState(true);\n const [imgLoaded, setImgLoaded] = useState(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const containerSize = useContainerQuery(containerRef);\n\n const entityCardCount =\n containerSize === 'xs' || containerSize === 'sm' ? 2 : 4;\n\n const illustrationWidthMap: Partial<Record<ContainerSize, number>> = {\n md: 180,\n lg: 220,\n xl: 266,\n };\n const illustrationWidth = illustrationWidthMap[containerSize] ?? 266;\n\n const visibleEntitiesCount = (() => {\n const isWide =\n containerSize === 'xl' ||\n containerSize === 'lg' ||\n containerSize === 'md';\n\n if (!isWide) return entityCardCount;\n\n return isRemoveFirstCard ? entityCardCount : entityCardCount - 2;\n })();\n\n useEffect(() => {\n const isUserDismissedEntityIllustration =\n hasEntityIllustrationUserDismissed(displayName);\n setIsRemoveFirstCard(isUserDismissedEntityIllustration);\n }, [displayName]);\n\n const handleClose = useCallback(() => {\n setShowDiscoveryCard(false);\n setTimeout(() => {\n addDismissedEntityIllustrationUsers(displayName);\n setIsRemoveFirstCard(true);\n }, 500);\n }, [displayName]);\n\n const { data, error, isLoading } = useEntities({\n kind: ['Component', 'API', 'Resource', 'System'],\n });\n\n const entities = data?.items ?? [];\n\n let content: ReactNode;\n\n if (isLoading) {\n content = (\n <Box\n sx={{\n height: '100%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <CircularProgress />\n </Box>\n );\n } else if (!data) {\n content = (\n <WarningPanel severity=\"error\" title={t('entities.fetchError')}>\n <CodeSnippet\n language=\"text\"\n text={error?.toString() ?? t('entities.error')}\n />\n </WarningPanel>\n );\n } else {\n content = (\n <Box sx={{ padding: '8px 8px 8px 0' }}>\n <Fragment>\n <Grid container spacing={1} alignItems=\"stretch\">\n {/* hiding discovery card on small containers */}\n {!isRemoveFirstCard &&\n !profileLoading &&\n containerSize !== 'xs' &&\n containerSize !== 'sm' && (\n <Grid item sx={containerGridItemSx({ md: 4 })}>\n <Card\n elevation={0}\n sx={{\n height: '100%',\n border: `1px solid ${theme.palette.grey[400]}`,\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n position: 'relative',\n transition:\n 'opacity 0.5s ease-out, transform 0.5s ease-in-out',\n opacity: showDiscoveryCard ? 1 : 0,\n transform: showDiscoveryCard\n ? 'translateX(0)'\n : 'translateX(-50px)',\n }}\n >\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n }}\n >\n {!imgLoaded && (\n <Skeleton\n variant=\"rectangular\"\n height={300}\n sx={{\n borderRadius: 3,\n width: illustrationWidth,\n }}\n />\n )}\n <Box\n component=\"img\"\n src={HomePageEntityIllustration}\n onLoad={() => setImgLoaded(true)}\n alt=\"\"\n height={300}\n sx={{\n width: illustrationWidth,\n }}\n />\n <Box sx={{ p: 2 }}>\n <Box sx={{ p: 2 }}>\n <Typography variant=\"body2\" paragraph>\n {t('entities.description')}\n </Typography>\n </Box>\n {entities?.length > 0 && (\n <IconButton\n onClick={handleClose}\n aria-label={t('entities.close')}\n style={{\n position: 'absolute',\n top: '8px',\n right: '8px',\n }}\n >\n <CloseIcon\n style={{ width: '16px', height: '16px' }}\n />\n </IconButton>\n )}\n </Box>\n </Box>\n </Card>\n </Grid>\n )}\n {entities?.slice(0, visibleEntitiesCount).map((item: any) => (\n <Grid\n item\n sx={containerGridItemSx({\n xs: 12,\n sm: 6,\n md: isRemoveFirstCard ? 3 : 4,\n })}\n key={item.metadata.name}\n >\n <EntityCard\n entity={item}\n title={item.metadata.title ?? item.metadata.name}\n version=\"latest\"\n description={item.metadata.description ?? ''}\n tags={item.metadata?.tags ?? []}\n kind={item.kind}\n />\n </Grid>\n ))}\n {entities?.length === 0 && (\n <Grid\n item\n sx={containerGridItemSx({\n sm: 12,\n md: isRemoveFirstCard ? 12 : 8,\n })}\n >\n <Box\n sx={{\n height: '100%',\n minHeight: 300,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: muiTheme =>\n `1px solid ${muiTheme.palette.grey[400]}`,\n borderRadius: 3,\n overflow: 'hidden',\n }}\n >\n <CardContent>\n <Typography sx={{ fontSize: '1.125rem', fontWeight: 500 }}>\n {t('entities.empty')}\n </Typography>\n <Typography\n sx={{\n fontSize: '0.875rem',\n fontWeight: 400,\n mt: '20px',\n mb: '16px',\n }}\n >\n {t('entities.emptyDescription')}\n </Typography>\n <StyledLink to=\"/catalog-import\" underline=\"none\">\n {t('entities.register')}\n </StyledLink>\n </CardContent>\n </Box>\n </Grid>\n )}\n </Grid>\n </Fragment>\n </Box>\n );\n }\n\n return (\n <Card\n elevation={0}\n sx={{\n padding: '24px',\n border: muitheme => `1px solid ${muitheme.palette.grey[300]}`,\n containerType: 'inline-size',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <Typography\n variant=\"h3\"\n sx={{\n display: 'flex',\n alignItems: 'center',\n fontWeight: '500',\n fontSize: '1.5rem',\n flexShrink: 0,\n }}\n >\n {t('entities.title')}\n </Typography>\n <Box\n ref={containerRef}\n sx={{\n flex: 1,\n minHeight: 0,\n overflowY: 'auto',\n mt: 1,\n }}\n >\n {content}\n {entities?.length > 0 && (\n <Box sx={{ pt: 2 }}>\n <ViewMoreLink to=\"/catalog\">\n <Trans\n message=\"entities.viewAll\"\n params={{ count: data?.totalItems?.toString() || '' }}\n />\n </ViewMoreLink>\n </Box>\n )}\n </Box>\n </Card>\n );\n};\n"],"names":["BackstageLink"],"mappings":"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"}
1
+ {"version":3,"file":"EntitySection.esm.js","sources":["../../../src/components/EntitySection/EntitySection.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 */\nimport type { ReactNode } from 'react';\n\nimport { useState, useEffect, Fragment, useRef, useCallback } from 'react';\n\nimport {\n CodeSnippet,\n WarningPanel,\n Link as BackstageLink,\n} from '@backstage/core-components';\nimport { useUserProfile } from '@backstage/plugin-user-settings';\n\nimport Grid from '@mui/material/Grid';\nimport Box from '@mui/material/Box';\nimport Card from '@mui/material/Card';\nimport IconButton from '@mui/material/IconButton';\nimport Skeleton from '@mui/material/Skeleton';\nimport Typography from '@mui/material/Typography';\nimport CloseIcon from '@mui/icons-material/Close';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport CardContent from '@mui/material/CardContent';\nimport { useTheme, styled } from '@mui/material/styles';\n\nimport EntityCard from './EntityCard';\nimport { ViewMoreLink } from './ViewMoreLink';\nimport HomePageEntityIllustration from '../../images/homepage-entities-1.svg';\nimport { useEntities } from '../../hooks/useEntities';\nimport {\n addDismissedEntityIllustrationUsers,\n hasEntityIllustrationUserDismissed,\n} from '../../utils/utils';\nimport { useTranslation } from '../../hooks/useTranslation';\nimport { containerGridItemSx } from '../../utils/GridItem';\nimport {\n useContainerQuery,\n type ContainerSize,\n} from '../../hooks/useContainerQuery';\n\nconst StyledLink = styled(BackstageLink)(({ theme }) => ({\n textDecoration: 'none',\n padding: theme.spacing(1, 1.5),\n fontSize: '16px',\n display: 'inline-flex',\n border: `1px solid ${theme.palette.primary.main}`,\n borderRadius: 4,\n}));\n\nexport const EntitySection = () => {\n const theme = useTheme();\n const { t } = useTranslation();\n const { displayName, loading: profileLoading } = useUserProfile();\n const [isRemoveFirstCard, setIsRemoveFirstCard] = useState(false);\n const [showDiscoveryCard, setShowDiscoveryCard] = useState(true);\n const [imgLoaded, setImgLoaded] = useState(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const containerSize = useContainerQuery(containerRef);\n\n const entityCardCount =\n containerSize === 'xs' || containerSize === 'sm' ? 2 : 4;\n\n const illustrationWidthMap: Partial<Record<ContainerSize, number>> = {\n md: 180,\n lg: 220,\n xl: 266,\n };\n const illustrationWidth = illustrationWidthMap[containerSize] ?? 266;\n\n const visibleEntitiesCount = (() => {\n const isWide =\n containerSize === 'xl' ||\n containerSize === 'lg' ||\n containerSize === 'md';\n\n if (!isWide) return entityCardCount;\n\n return isRemoveFirstCard ? entityCardCount : entityCardCount - 2;\n })();\n\n useEffect(() => {\n const isUserDismissedEntityIllustration =\n hasEntityIllustrationUserDismissed(displayName);\n setIsRemoveFirstCard(isUserDismissedEntityIllustration);\n }, [displayName]);\n\n const handleClose = useCallback(() => {\n setShowDiscoveryCard(false);\n setTimeout(() => {\n addDismissedEntityIllustrationUsers(displayName);\n setIsRemoveFirstCard(true);\n }, 500);\n }, [displayName]);\n\n const { data, error, isLoading } = useEntities({\n kind: ['Component', 'API', 'Resource', 'System'],\n });\n\n const entities = data?.items ?? [];\n\n let content: ReactNode;\n\n if (isLoading) {\n content = (\n <Box\n sx={{\n height: '100%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <CircularProgress />\n </Box>\n );\n } else if (!data) {\n content = (\n <WarningPanel severity=\"error\" title={t('entities.fetchError')}>\n <CodeSnippet\n language=\"text\"\n text={error?.toString() ?? t('entities.error')}\n />\n </WarningPanel>\n );\n } else {\n content = (\n <Box sx={{ padding: '8px 8px 8px 0' }}>\n <Fragment>\n <Grid container spacing={1} alignItems=\"stretch\">\n {/* hiding discovery card on small containers */}\n {!isRemoveFirstCard &&\n !profileLoading &&\n containerSize !== 'xs' &&\n containerSize !== 'sm' && (\n <Grid item sx={containerGridItemSx({ md: 4 })}>\n <Card\n elevation={0}\n sx={{\n height: '100%',\n border: `1px solid ${theme.palette.grey[400]}`,\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n position: 'relative',\n transition:\n 'opacity 0.5s ease-out, transform 0.5s ease-in-out',\n opacity: showDiscoveryCard ? 1 : 0,\n transform: showDiscoveryCard\n ? 'translateX(0)'\n : 'translateX(-50px)',\n }}\n >\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n }}\n >\n {!imgLoaded && (\n <Skeleton\n variant=\"rectangular\"\n height={300}\n sx={{\n borderRadius: 3,\n width: illustrationWidth,\n }}\n />\n )}\n <Box\n component=\"img\"\n src={HomePageEntityIllustration}\n onLoad={() => setImgLoaded(true)}\n alt=\"\"\n height={300}\n sx={{\n width: illustrationWidth,\n }}\n />\n <Box sx={{ p: 2 }}>\n <Box sx={{ p: 2 }}>\n <Typography variant=\"body2\" paragraph>\n {t('entities.description')}\n </Typography>\n </Box>\n {entities?.length > 0 && (\n <IconButton\n onClick={handleClose}\n aria-label={t('entities.close')}\n style={{\n position: 'absolute',\n top: '8px',\n right: '8px',\n }}\n >\n <CloseIcon\n style={{ width: '16px', height: '16px' }}\n />\n </IconButton>\n )}\n </Box>\n </Box>\n </Card>\n </Grid>\n )}\n {entities?.slice(0, visibleEntitiesCount).map((item: any) => (\n <Grid\n item\n sx={containerGridItemSx({\n xs: 12,\n sm: 6,\n md: isRemoveFirstCard ? 3 : 4,\n })}\n key={item.metadata.name}\n >\n <EntityCard\n entity={item}\n title={item.metadata.title ?? item.metadata.name}\n version=\"latest\"\n description={item.metadata.description ?? ''}\n tags={item.metadata?.tags ?? []}\n kind={item.kind}\n />\n </Grid>\n ))}\n {entities?.length === 0 && (\n <Grid\n item\n sx={containerGridItemSx({\n sm: 12,\n md: isRemoveFirstCard ? 12 : 8,\n })}\n >\n <Box\n sx={{\n height: '100%',\n minHeight: 300,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: muiTheme =>\n `1px solid ${muiTheme.palette.grey[400]}`,\n borderRadius: 3,\n overflow: 'hidden',\n }}\n >\n <CardContent>\n <Typography sx={{ fontSize: '1.125rem', fontWeight: 500 }}>\n {t('entities.empty')}\n </Typography>\n <Typography\n sx={{\n fontSize: '0.875rem',\n fontWeight: 400,\n mt: '20px',\n mb: '16px',\n }}\n >\n {t('entities.emptyDescription')}\n </Typography>\n <StyledLink to=\"/catalog-import\" underline=\"none\">\n {t('entities.register')}\n </StyledLink>\n </CardContent>\n </Box>\n </Grid>\n )}\n </Grid>\n </Fragment>\n </Box>\n );\n }\n\n return (\n <Card\n elevation={0}\n sx={{\n padding: '24px',\n border: muitheme => `1px solid ${muitheme.palette.grey[300]}`,\n containerType: 'inline-size',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <Typography\n variant=\"h3\"\n sx={{\n display: 'flex',\n alignItems: 'center',\n fontWeight: '500',\n fontSize: '1.5rem',\n flexShrink: 0,\n }}\n >\n {t('entities.title')}\n </Typography>\n <Box\n ref={containerRef}\n sx={{\n flex: 1,\n minHeight: 0,\n overflowY: 'auto',\n mt: 1,\n }}\n >\n {content}\n {entities?.length > 0 && (\n <Box sx={{ pt: 2 }}>\n <ViewMoreLink to=\"/catalog\">\n {t('entities.browseTheCatalog')}\n </ViewMoreLink>\n </Box>\n )}\n </Box>\n </Card>\n );\n};\n"],"names":["BackstageLink"],"mappings":"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"}
@@ -1,9 +1,12 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { EmptyState, Page, Content } from '@backstage/core-components';
2
+ import { EmptyState, Page, Content, Progress } from '@backstage/core-components';
3
+ import { useDefaultWidgets } from '../hooks/useDefaultWidgets.esm.js';
3
4
  import { useTranslation } from '../hooks/useTranslation.esm.js';
5
+ import { CustomizableGrid } from './CustomizableGrid.esm.js';
6
+ import { DefaultWidgetsCustomizableGrid } from './DefaultWidgetsCustomizableGrid.esm.js';
7
+ import { DefaultWidgetsReadOnlyGrid } from './DefaultWidgetsReadOnlyGrid.esm.js';
4
8
  import { Header } from './Header.esm.js';
5
9
  import { ReadOnlyGrid } from './ReadOnlyGrid.esm.js';
6
- import { CustomizableGrid } from './CustomizableGrid.esm.js';
7
10
 
8
11
  const HomePage = ({
9
12
  mountPoints,
@@ -11,8 +14,29 @@ const HomePage = ({
11
14
  ...otherProps
12
15
  }) => {
13
16
  const { t } = useTranslation();
17
+ const { defaultWidgets, loading } = useDefaultWidgets();
14
18
  let content;
15
- if (mountPoints.length === 0) {
19
+ if (loading) {
20
+ content = /* @__PURE__ */ jsx(Progress, {});
21
+ } else if (defaultWidgets && defaultWidgets.length > 0) {
22
+ if (customizable) {
23
+ content = /* @__PURE__ */ jsx(
24
+ DefaultWidgetsCustomizableGrid,
25
+ {
26
+ defaultWidgets,
27
+ mountPoints
28
+ }
29
+ );
30
+ } else {
31
+ content = /* @__PURE__ */ jsx(
32
+ DefaultWidgetsReadOnlyGrid,
33
+ {
34
+ defaultWidgets,
35
+ mountPoints
36
+ }
37
+ );
38
+ }
39
+ } else if (mountPoints.length === 0) {
16
40
  content = /* @__PURE__ */ jsx(EmptyState, { title: t("homePage.empty"), missing: "content" });
17
41
  } else if (customizable) {
18
42
  content = /* @__PURE__ */ jsx(CustomizableGrid, { mountPoints });
@@ -1 +1 @@
1
- {"version":3,"file":"HomePage.esm.js","sources":["../../src/components/HomePage.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';\n\nimport { useTranslation } from '../hooks/useTranslation';\nimport { HomePageCardMountPoint } from '../types';\nimport { Header, HeaderProps } from './Header';\nimport { ReadOnlyGrid } from './ReadOnlyGrid';\nimport { CustomizableGrid } from './CustomizableGrid';\n\nexport interface HomePageProps extends HeaderProps {\n mountPoints: HomePageCardMountPoint[];\n customizable: boolean;\n}\n\nexport const HomePage = ({\n mountPoints,\n customizable,\n ...otherProps\n}: HomePageProps) => {\n const { t } = useTranslation();\n\n let content: React.ReactNode;\n if (mountPoints.length === 0) {\n content = <EmptyState title={t('homePage.empty')} missing=\"content\" />;\n } else if (customizable) {\n content = <CustomizableGrid mountPoints={mountPoints} />;\n } else {\n content = <ReadOnlyGrid mountPoints={mountPoints} />;\n }\n\n return (\n <Page themeId=\"home\">\n <Header title={t('header.welcome')} {...otherProps} />\n <Content>{content}</Content>\n </Page>\n );\n};\n"],"names":[],"mappings":";;;;;;;AA6BO,MAAM,WAAW,CAAC;AAAA,EACvB,WAAA;AAAA,EACA,YAAA;AAAA,EACA,GAAG;AACL,CAAqB,KAAA;AACnB,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,cAAe,EAAA;AAE7B,EAAI,IAAA,OAAA;AACJ,EAAI,IAAA,WAAA,CAAY,WAAW,CAAG,EAAA;AAC5B,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,oBAAiB,WAA0B,EAAA,CAAA;AAAA,GACjD,MAAA;AACL,IAAU,OAAA,mBAAA,GAAA,CAAC,gBAAa,WAA0B,EAAA,CAAA;AAAA;AAGpD,EACE,uBAAA,IAAA,CAAC,IAAK,EAAA,EAAA,OAAA,EAAQ,MACZ,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,UAAO,KAAO,EAAA,CAAA,CAAE,gBAAgB,CAAA,EAAI,GAAG,UAAY,EAAA,CAAA;AAAA,oBACpD,GAAA,CAAC,WAAS,QAAQ,EAAA,OAAA,EAAA;AAAA,GACpB,EAAA,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"HomePage.esm.js","sources":["../../src/components/HomePage.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 {\n Content,\n EmptyState,\n Page,\n Progress,\n} from '@backstage/core-components';\n\nimport { useDefaultWidgets } from '../hooks/useDefaultWidgets';\nimport { useTranslation } from '../hooks/useTranslation';\nimport { HomePageCardMountPoint } from '../types';\nimport { CustomizableGrid } from './CustomizableGrid';\nimport { DefaultWidgetsCustomizableGrid } from './DefaultWidgetsCustomizableGrid';\nimport { DefaultWidgetsReadOnlyGrid } from './DefaultWidgetsReadOnlyGrid';\nimport { Header, HeaderProps } from './Header';\nimport { ReadOnlyGrid } from './ReadOnlyGrid';\n\nexport interface HomePageProps extends HeaderProps {\n mountPoints: HomePageCardMountPoint[];\n customizable: boolean;\n}\n\nexport const HomePage = ({\n mountPoints,\n customizable,\n ...otherProps\n}: HomePageProps) => {\n const { t } = useTranslation();\n const { defaultWidgets, loading } = useDefaultWidgets();\n\n let content: React.ReactNode;\n if (loading) {\n content = <Progress />;\n } else if (defaultWidgets && defaultWidgets.length > 0) {\n if (customizable) {\n content = (\n <DefaultWidgetsCustomizableGrid\n defaultWidgets={defaultWidgets}\n mountPoints={mountPoints}\n />\n );\n } else {\n content = (\n <DefaultWidgetsReadOnlyGrid\n defaultWidgets={defaultWidgets}\n mountPoints={mountPoints}\n />\n );\n }\n } else if (mountPoints.length === 0) {\n content = <EmptyState title={t('homePage.empty')} missing=\"content\" />;\n } else if (customizable) {\n content = <CustomizableGrid mountPoints={mountPoints} />;\n } else {\n content = <ReadOnlyGrid mountPoints={mountPoints} />;\n }\n\n return (\n <Page themeId=\"home\">\n <Header title={t('header.welcome')} {...otherProps} />\n <Content>{content}</Content>\n </Page>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;AAqCO,MAAM,WAAW,CAAC;AAAA,EACvB,WAAA;AAAA,EACA,YAAA;AAAA,EACA,GAAG;AACL,CAAqB,KAAA;AACnB,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,cAAe,EAAA;AAC7B,EAAA,MAAM,EAAE,cAAA,EAAgB,OAAQ,EAAA,GAAI,iBAAkB,EAAA;AAEtD,EAAI,IAAA,OAAA;AACJ,EAAA,IAAI,OAAS,EAAA;AACX,IAAA,OAAA,uBAAW,QAAS,EAAA,EAAA,CAAA;AAAA,GACX,MAAA,IAAA,cAAA,IAAkB,cAAe,CAAA,MAAA,GAAS,CAAG,EAAA;AACtD,IAAA,IAAI,YAAc,EAAA;AAChB,MACE,OAAA,mBAAA,GAAA;AAAA,QAAC,8BAAA;AAAA,QAAA;AAAA,UACC,cAAA;AAAA,UACA;AAAA;AAAA,OACF;AAAA,KAEG,MAAA;AACL,MACE,OAAA,mBAAA,GAAA;AAAA,QAAC,0BAAA;AAAA,QAAA;AAAA,UACC,cAAA;AAAA,UACA;AAAA;AAAA,OACF;AAAA;AAEJ,GACF,MAAA,IAAW,WAAY,CAAA,MAAA,KAAW,CAAG,EAAA;AACnC,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,oBAAiB,WAA0B,EAAA,CAAA;AAAA,GACjD,MAAA;AACL,IAAU,OAAA,mBAAA,GAAA,CAAC,gBAAa,WAA0B,EAAA,CAAA;AAAA;AAGpD,EACE,uBAAA,IAAA,CAAC,IAAK,EAAA,EAAA,OAAA,EAAQ,MACZ,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,UAAO,KAAO,EAAA,CAAA,CAAE,gBAAgB,CAAA,EAAI,GAAG,UAAY,EAAA,CAAA;AAAA,oBACpD,GAAA,CAAC,WAAS,QAAQ,EAAA,OAAA,EAAA;AAAA,GACpB,EAAA,CAAA;AAEJ;;;;"}
@@ -19,7 +19,9 @@ const resolveContainerSize = (width) => {
19
19
  if (width >= CONTAINER_BREAKPOINTS.sm) return "sm";
20
20
  return "xs";
21
21
  };
22
- const useContainerQuery = (ref) => {
22
+ const NOTIFY_WIDTH_EPSILON = 0.5;
23
+ const useContainerQuery = (ref, options) => {
24
+ const notifyWindowResize = options?.notifyWindowResize ?? false;
23
25
  const [containerSize, setContainerSize] = useState("lg");
24
26
  useLayoutEffect(() => {
25
27
  const el = ref.current;
@@ -28,14 +30,47 @@ const useContainerQuery = (ref) => {
28
30
  const next = resolveContainerSize(width);
29
31
  setContainerSize((prev) => prev === next ? prev : next);
30
32
  };
31
- updateSize(el.getBoundingClientRect().width);
33
+ let lastNotifiedWidth = null;
34
+ let resizeNotifyRafId = null;
35
+ let pendingNotifyWidth = null;
36
+ const scheduleWindowResizeNotify = (width) => {
37
+ if (!notifyWindowResize || typeof window === "undefined") {
38
+ return;
39
+ }
40
+ pendingNotifyWidth = width;
41
+ if (resizeNotifyRafId !== null) {
42
+ return;
43
+ }
44
+ resizeNotifyRafId = window.requestAnimationFrame(() => {
45
+ resizeNotifyRafId = null;
46
+ const w = pendingNotifyWidth;
47
+ if (w === null) {
48
+ return;
49
+ }
50
+ if (lastNotifiedWidth !== null && Math.abs(w - lastNotifiedWidth) < NOTIFY_WIDTH_EPSILON) {
51
+ return;
52
+ }
53
+ lastNotifiedWidth = w;
54
+ window.dispatchEvent(new Event("resize"));
55
+ });
56
+ };
57
+ const onObservedWidth = (width) => {
58
+ updateSize(width);
59
+ scheduleWindowResizeNotify(width);
60
+ };
61
+ onObservedWidth(el.getBoundingClientRect().width);
32
62
  const observer = new ResizeObserver((entries) => {
33
63
  if (!entries.length) return;
34
- updateSize(entries[0].contentRect.width);
64
+ onObservedWidth(entries[0].contentRect.width);
35
65
  });
36
66
  observer.observe(el);
37
- return () => observer.disconnect();
38
- }, [ref]);
67
+ return () => {
68
+ observer.disconnect();
69
+ if (resizeNotifyRafId !== null && typeof window !== "undefined") {
70
+ window.cancelAnimationFrame(resizeNotifyRafId);
71
+ }
72
+ };
73
+ }, [ref, notifyWindowResize]);
39
74
  return containerSize;
40
75
  };
41
76
 
@@ -1 +1 @@
1
- {"version":3,"file":"useContainerQuery.esm.js","sources":["../../src/hooks/useContainerQuery.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\n/*\n * A React hook that observes the width of a DOM element and\n * returns a responsive \"container size\" (`xs | sm | md | lg | xl`)\n * based on predefined breakpoints.\n *\n * Unlike window-based media queries, this hook is **container-based**:\n * the returned size depends on the element's own width, not the viewport.\n *\n * Internally, it:\n * - Reads the element's initial layout width on mount\n * - Uses ResizeObserver to track width changes\n * - Maps the width to MUI-like breakpoints\n * - Updates state only when the breakpoint actually changes\n * */\n\nimport { useLayoutEffect, useState, RefObject } from 'react';\n\n// Container Breakpoints (MUI-like, but container-based)\nexport type ContainerSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\nexport const CONTAINER_BREAKPOINTS = {\n xs: 0, // <600\n sm: 600, // ≥600\n md: 900, // ≥900\n lg: 1200, // ≥1200\n xl: 1536, // ≥1536\n} as const;\n\n// Width → containerSize mapper\nconst resolveContainerSize = (width: number): ContainerSize => {\n if (width >= CONTAINER_BREAKPOINTS.xl) return 'xl';\n if (width >= CONTAINER_BREAKPOINTS.lg) return 'lg';\n if (width >= CONTAINER_BREAKPOINTS.md) return 'md';\n if (width >= CONTAINER_BREAKPOINTS.sm) return 'sm';\n return 'xs';\n};\n\n// Hook: useContainerQuery\n\nexport const useContainerQuery = (\n ref: RefObject<HTMLElement>,\n): ContainerSize => {\n const [containerSize, setContainerSize] = useState<ContainerSize>('lg');\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return undefined;\n\n const updateSize = (width: number) => {\n const next = resolveContainerSize(width);\n setContainerSize(prev => (prev === next ? prev : next));\n };\n\n // Initial read\n updateSize(el.getBoundingClientRect().width);\n\n const observer = new ResizeObserver(entries => {\n if (!entries.length) return;\n updateSize(entries[0].contentRect.width);\n });\n\n observer.observe(el);\n return () => observer.disconnect();\n }, [ref]);\n\n return containerSize;\n};\n"],"names":[],"mappings":";;AAoCO,MAAM,qBAAwB,GAAA;AAAA,EACnC,EAAI,EAAA,CAAA;AAAA;AAAA,EACJ,EAAI,EAAA,GAAA;AAAA;AAAA,EACJ,EAAI,EAAA,GAAA;AAAA;AAAA,EACJ,EAAI,EAAA,IAAA;AAAA;AAAA,EACJ,EAAI,EAAA;AAAA;AACN;AAGA,MAAM,oBAAA,GAAuB,CAAC,KAAiC,KAAA;AAC7D,EAAI,IAAA,KAAA,IAAS,qBAAsB,CAAA,EAAA,EAAW,OAAA,IAAA;AAC9C,EAAI,IAAA,KAAA,IAAS,qBAAsB,CAAA,EAAA,EAAW,OAAA,IAAA;AAC9C,EAAI,IAAA,KAAA,IAAS,qBAAsB,CAAA,EAAA,EAAW,OAAA,IAAA;AAC9C,EAAI,IAAA,KAAA,IAAS,qBAAsB,CAAA,EAAA,EAAW,OAAA,IAAA;AAC9C,EAAO,OAAA,IAAA;AACT,CAAA;AAIa,MAAA,iBAAA,GAAoB,CAC/B,GACkB,KAAA;AAClB,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAwB,IAAI,CAAA;AAEtE,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,MAAM,KAAK,GAAI,CAAA,OAAA;AACf,IAAI,IAAA,CAAC,IAAW,OAAA,SAAA;AAEhB,IAAM,MAAA,UAAA,GAAa,CAAC,KAAkB,KAAA;AACpC,MAAM,MAAA,IAAA,GAAO,qBAAqB,KAAK,CAAA;AACvC,MAAA,gBAAA,CAAiB,CAAS,IAAA,KAAA,IAAA,KAAS,IAAO,GAAA,IAAA,GAAO,IAAK,CAAA;AAAA,KACxD;AAGA,IAAW,UAAA,CAAA,EAAA,CAAG,qBAAsB,EAAA,CAAE,KAAK,CAAA;AAE3C,IAAM,MAAA,QAAA,GAAW,IAAI,cAAA,CAAe,CAAW,OAAA,KAAA;AAC7C,MAAI,IAAA,CAAC,QAAQ,MAAQ,EAAA;AACrB,MAAA,UAAA,CAAW,OAAQ,CAAA,CAAC,CAAE,CAAA,WAAA,CAAY,KAAK,CAAA;AAAA,KACxC,CAAA;AAED,IAAA,QAAA,CAAS,QAAQ,EAAE,CAAA;AACnB,IAAO,OAAA,MAAM,SAAS,UAAW,EAAA;AAAA,GACnC,EAAG,CAAC,GAAG,CAAC,CAAA;AAER,EAAO,OAAA,aAAA;AACT;;;;"}
1
+ {"version":3,"file":"useContainerQuery.esm.js","sources":["../../src/hooks/useContainerQuery.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\n/*\n * A React hook that observes the width of a DOM element and\n * returns a responsive \"container size\" (`xs | sm | md | lg | xl`)\n * based on predefined breakpoints.\n *\n * Unlike window-based media queries, this hook is **container-based**:\n * the returned size depends on the element's own width, not the viewport.\n *\n * Internally, it:\n * - Reads the element's initial layout width on mount\n * - Uses ResizeObserver to track width changes\n * - Maps the width to MUI-like breakpoints\n * - Updates state only when the breakpoint actually changes\n *\n * Optional `notifyWindowResize`: when true, also dispatches a `window` `resize`\n * event when the observed width changes meaningfully. Dispatches are coalesced\n * to at most once per animation frame to avoid resize storms during continuous\n * container resizes. That lets react-grid-layout's `WidthProvider` (used by\n * `CustomHomepageGrid`) remeasure when the main column shrinks without a\n * viewport resize (e.g. RHDH docked drawer).\n * */\n\nimport { useLayoutEffect, useState, RefObject } from 'react';\n\nexport type UseContainerQueryOptions = {\n notifyWindowResize?: boolean;\n};\n\n// Container Breakpoints (MUI-like, but container-based)\nexport type ContainerSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\nexport const CONTAINER_BREAKPOINTS = {\n xs: 0, // <600\n sm: 600, // ≥600\n md: 900, // ≥900\n lg: 1200, // ≥1200\n xl: 1536, // ≥1536\n} as const;\n\n// Width → containerSize mapper\nconst resolveContainerSize = (width: number): ContainerSize => {\n if (width >= CONTAINER_BREAKPOINTS.xl) return 'xl';\n if (width >= CONTAINER_BREAKPOINTS.lg) return 'lg';\n if (width >= CONTAINER_BREAKPOINTS.md) return 'md';\n if (width >= CONTAINER_BREAKPOINTS.sm) return 'sm';\n return 'xs';\n};\n\n/** Ignore sub-pixel jitter when deciding whether to fire a synthetic resize. */\nconst NOTIFY_WIDTH_EPSILON = 0.5;\n\n// Hook: useContainerQuery\n\nexport const useContainerQuery = (\n ref: RefObject<HTMLElement>,\n options?: UseContainerQueryOptions,\n): ContainerSize => {\n const notifyWindowResize = options?.notifyWindowResize ?? false;\n const [containerSize, setContainerSize] = useState<ContainerSize>('lg');\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return undefined;\n\n const updateSize = (width: number) => {\n const next = resolveContainerSize(width);\n setContainerSize(prev => (prev === next ? prev : next));\n };\n\n let lastNotifiedWidth: number | null = null;\n let resizeNotifyRafId: number | null = null;\n let pendingNotifyWidth: number | null = null;\n\n const scheduleWindowResizeNotify = (width: number) => {\n if (!notifyWindowResize || typeof window === 'undefined') {\n return;\n }\n pendingNotifyWidth = width;\n if (resizeNotifyRafId !== null) {\n return;\n }\n resizeNotifyRafId = window.requestAnimationFrame(() => {\n resizeNotifyRafId = null;\n const w = pendingNotifyWidth;\n if (w === null) {\n return;\n }\n if (\n lastNotifiedWidth !== null &&\n Math.abs(w - lastNotifiedWidth) < NOTIFY_WIDTH_EPSILON\n ) {\n return;\n }\n lastNotifiedWidth = w;\n window.dispatchEvent(new Event('resize'));\n });\n };\n\n const onObservedWidth = (width: number) => {\n updateSize(width);\n scheduleWindowResizeNotify(width);\n };\n\n onObservedWidth(el.getBoundingClientRect().width);\n\n const observer = new ResizeObserver(entries => {\n if (!entries.length) return;\n onObservedWidth(entries[0].contentRect.width);\n });\n\n observer.observe(el);\n return () => {\n observer.disconnect();\n if (resizeNotifyRafId !== null && typeof window !== 'undefined') {\n window.cancelAnimationFrame(resizeNotifyRafId);\n }\n };\n }, [ref, notifyWindowResize]);\n\n return containerSize;\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { useApi } from '@backstage/core-plugin-api';
2
+ import useAsync from 'react-use/lib/useAsync';
3
+ import { defaultWidgetsApiRef } from '../api/DefaultWidgetsApiClient.esm.js';
4
+
5
+ const useDefaultWidgets = () => {
6
+ const defaultWidgetsApi = useApi(defaultWidgetsApiRef);
7
+ const { value, loading, error } = useAsync(
8
+ () => defaultWidgetsApi.getDefaultWidgets(),
9
+ [defaultWidgetsApi]
10
+ );
11
+ return {
12
+ defaultWidgets: value?.items,
13
+ loading,
14
+ error
15
+ };
16
+ };
17
+
18
+ export { useDefaultWidgets };
19
+ //# sourceMappingURL=useDefaultWidgets.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDefaultWidgets.esm.js","sources":["../../src/hooks/useDefaultWidgets.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 { useApi } from '@backstage/core-plugin-api';\nimport useAsync from 'react-use/lib/useAsync';\n\nimport {\n defaultWidgetsApiRef,\n type VisibleDefaultWidget,\n} from '../api/DefaultWidgetsApiClient';\n\nexport const useDefaultWidgets = (): {\n defaultWidgets: VisibleDefaultWidget[] | undefined;\n loading: boolean;\n error: Error | undefined;\n} => {\n const defaultWidgetsApi = useApi(defaultWidgetsApiRef);\n\n const { value, loading, error } = useAsync(\n () => defaultWidgetsApi.getDefaultWidgets(),\n [defaultWidgetsApi],\n );\n\n return {\n defaultWidgets: value?.items,\n loading,\n error,\n };\n};\n"],"names":[],"mappings":";;;;AAwBO,MAAM,oBAAoB,MAI5B;AACH,EAAM,MAAA,iBAAA,GAAoB,OAAO,oBAAoB,CAAA;AAErD,EAAA,MAAM,EAAE,KAAA,EAAO,OAAS,EAAA,KAAA,EAAU,GAAA,QAAA;AAAA,IAChC,MAAM,kBAAkB,iBAAkB,EAAA;AAAA,IAC1C,CAAC,iBAAiB;AAAA,GACpB;AAEA,EAAO,OAAA;AAAA,IACL,gBAAgB,KAAO,EAAA,KAAA;AAAA,IACvB,OAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,18 +1,11 @@
1
- import { ClockConfig, StarredEntitiesProps, VisitedByTypeProps, FeaturedDocsCardProps } from '@backstage/plugin-home';
2
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
2
  import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
4
3
  import { ComponentType } from 'react';
4
+ import { ClockConfig, StarredEntitiesProps, VisitedByTypeProps, FeaturedDocsCardProps } from '@backstage/plugin-home';
5
+ import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
6
+ import { DefaultWidgetsResponse } from '@red-hat-developer-hub/backstage-plugin-homepage-common';
5
7
  import { CardLayout, CardSettings } from '@backstage/plugin-home-react';
6
8
 
7
- /**
8
- * @public
9
- */
10
- interface WorldClockProps {
11
- worldClocks: ClockConfig[];
12
- timeFormat?: Intl.DateTimeFormatOptions;
13
- justifyContent?: 'space-between' | 'space-around';
14
- }
15
-
16
9
  /**
17
10
  * @public
18
11
  */
@@ -98,6 +91,17 @@ interface PlaceholderProps {
98
91
  debugContent?: string;
99
92
  }
100
93
 
94
+ /**
95
+ * @public
96
+ */
97
+ interface DefaultWidgetsApi {
98
+ getDefaultWidgets(): Promise<DefaultWidgetsResponse>;
99
+ }
100
+ /**
101
+ * @public
102
+ */
103
+ declare const defaultWidgetsApiRef: _backstage_frontend_plugin_api.ApiRef<DefaultWidgetsApi>;
104
+
101
105
  /**
102
106
  * @public
103
107
  */
@@ -157,6 +161,15 @@ interface HomePageCardMountPoint {
157
161
  enabled?: boolean;
158
162
  }
159
163
 
164
+ /**
165
+ * @public
166
+ */
167
+ interface WorldClockProps {
168
+ worldClocks: ClockConfig[];
169
+ timeFormat?: Intl.DateTimeFormatOptions;
170
+ justifyContent?: 'space-between' | 'space-around';
171
+ }
172
+
160
173
  /**
161
174
  * Dynamic Home Page Plugin
162
175
  * @public
@@ -241,4 +254,4 @@ declare const EntitySection: () => react_jsx_runtime.JSX.Element;
241
254
  */
242
255
  declare const TemplateSection: () => react_jsx_runtime.JSX.Element;
243
256
 
244
- export { Breakpoint, CatalogStarredEntitiesCard, DynamicCustomizableHomePage, type DynamicCustomizableHomePageProps, DynamicHomePage, type DynamicHomePageProps, EntitySection, FeaturedDocsCard, Headline, type HeadlineProps, type HomePageCardMountPoint, type HomePageCardMountPointConfig, JokeCard, type Layout, type LocalClockProps, Markdown, MarkdownCard, type MarkdownCardProps, type MarkdownProps, OnboardingSection, Placeholder, type PlaceholderProps, QuickAccessCard, type QuickAccessCardProps, RecentlyVisitedCard, SearchBar, type SearchBarProps, TemplateSection, TopVisitedCard, VisitListener, WorldClock, type WorldClockProps, dynamicHomePagePlugin };
257
+ export { Breakpoint, CatalogStarredEntitiesCard, type DefaultWidgetsApi, DynamicCustomizableHomePage, type DynamicCustomizableHomePageProps, DynamicHomePage, type DynamicHomePageProps, EntitySection, FeaturedDocsCard, Headline, type HeadlineProps, type HomePageCardMountPoint, type HomePageCardMountPointConfig, JokeCard, type Layout, type LocalClockProps, Markdown, MarkdownCard, type MarkdownCardProps, type MarkdownProps, OnboardingSection, Placeholder, type PlaceholderProps, QuickAccessCard, type QuickAccessCardProps, RecentlyVisitedCard, SearchBar, type SearchBarProps, TemplateSection, TopVisitedCard, VisitListener, WorldClock, type WorldClockProps, defaultWidgetsApiRef, dynamicHomePagePlugin };
package/dist/index.esm.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { CatalogStarredEntitiesCard, DynamicCustomizableHomePage, DynamicHomePage, EntitySection, FeaturedDocsCard, Headline, JokeCard, Markdown, MarkdownCard, OnboardingSection, Placeholder, QuickAccessCard, RecentlyVisitedCard, SearchBar, TemplateSection, TopVisitedCard, VisitListener, WorldClock, dynamicHomePagePlugin } from './plugin.esm.js';
2
+ export { defaultWidgetsApiRef } from './api/DefaultWidgetsApiClient.esm.js';
2
3
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,6 +1,7 @@
1
- import { createPlugin, createApiFactory, discoveryApiRef, configApiRef, identityApiRef, storageApiRef, createRoutableExtension, createComponentExtension } from '@backstage/core-plugin-api';
1
+ import { createPlugin, createApiFactory, discoveryApiRef, configApiRef, identityApiRef, fetchApiRef, storageApiRef, createRoutableExtension, createComponentExtension } from '@backstage/core-plugin-api';
2
2
  import { visitsApiRef, VisitsStorageApi } from '@backstage/plugin-home';
3
3
  import { rootRouteRef } from './routes.esm.js';
4
+ import { defaultWidgetsApiRef, DefaultWidgetsApiClient } from './api/DefaultWidgetsApiClient.esm.js';
4
5
  import { quickAccessApiRef, QuickAccessApiClient } from './api/QuickAccessApiClient.esm.js';
5
6
 
6
7
  const dynamicHomePagePlugin = createPlugin({
@@ -18,6 +19,14 @@ const dynamicHomePagePlugin = createPlugin({
18
19
  },
19
20
  factory: ({ discoveryApi, configApi, identityApi }) => new QuickAccessApiClient({ discoveryApi, configApi, identityApi })
20
21
  }),
22
+ createApiFactory({
23
+ api: defaultWidgetsApiRef,
24
+ deps: {
25
+ discoveryApi: discoveryApiRef,
26
+ fetchApi: fetchApiRef
27
+ },
28
+ factory: ({ discoveryApi, fetchApi }) => new DefaultWidgetsApiClient({ discoveryApi, fetchApi })
29
+ }),
21
30
  createApiFactory({
22
31
  api: visitsApiRef,
23
32
  deps: {
@@ -173,5 +182,5 @@ const TemplateSection = dynamicHomePagePlugin.provide(
173
182
  })
174
183
  );
175
184
 
176
- export { CatalogStarredEntitiesCard, DynamicCustomizableHomePage, DynamicHomePage, EntitySection, FeaturedDocsCard, Headline, JokeCard, Markdown, MarkdownCard, OnboardingSection, Placeholder, QuickAccessCard, RecentlyVisitedCard, SearchBar, TemplateSection, TopVisitedCard, VisitListener, WorldClock, dynamicHomePagePlugin };
185
+ export { CatalogStarredEntitiesCard, DynamicCustomizableHomePage, DynamicHomePage, EntitySection, FeaturedDocsCard, Headline, JokeCard, Markdown, MarkdownCard, OnboardingSection, Placeholder, QuickAccessCard, RecentlyVisitedCard, SearchBar, TemplateSection, TopVisitedCard, VisitListener, WorldClock, defaultWidgetsApiRef, dynamicHomePagePlugin };
177
186
  //# sourceMappingURL=plugin.esm.js.map