Skip to content

Commit ff397d8

Browse files
kmcfaulmcoker
andauthored
feat(Compass): add Compass components (#12093)
* feat(Compass): add Compass components * combine section and glass wrapper * update basic example and aria * back out panel for now * add full height modifier * feedback * remove prop * remove prop * chore: updates * use hardcoded modifier until css update * fix a11y in demo * working session updates * chore: bump core version * chore: update yarn.lock * use react-styles instead of hardcoded css classes * some cleanup * add some docs * update basic example css for borders * add tests, update inert * pr feedback, update demo background * bump core to .11 prerelease --------- Co-authored-by: mcoker <[email protected]>
1 parent 2d7afac commit ff397d8

34 files changed

+1557
-87
lines changed

packages/react-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"tslib": "^2.8.1"
5555
},
5656
"devDependencies": {
57-
"@patternfly/patternfly": "6.5.0-prerelease.9",
57+
"@patternfly/patternfly": "6.5.0-prerelease.11",
5858
"case-anything": "^3.1.2",
5959
"css": "^3.0.0",
6060
"fs-extra": "^11.3.0"
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { Drawer, DrawerContent, DrawerProps } from '../Drawer';
2+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
3+
import { css } from '@patternfly/react-styles';
4+
5+
import compassBackgroundImageLight from '@patternfly/react-tokens/dist/esm/c_compass_BackgroundImage_light';
6+
import compassBackgroundImageDark from '@patternfly/react-tokens/dist/esm/c_compass_BackgroundImage_dark';
7+
8+
export interface CompassProps extends React.HTMLProps<HTMLDivElement> {
9+
/** Additional classes added to the compass. */
10+
className?: string;
11+
/** Content placed at the top of the layout */
12+
header?: React.ReactNode;
13+
/** Flag indicating if the header is expanded */
14+
isHeaderExpanded?: boolean;
15+
/** Content placed at the horizontal start of the layout, before the main content */
16+
sidebarStart?: React.ReactNode;
17+
/** Flag indicating if the start sidebar is expanded */
18+
isSidebarStartExpanded?: boolean;
19+
/** Content placed at the center of the layout */
20+
main?: React.ReactNode;
21+
/** Content placed at the horizontal end of the layout, after the main content */
22+
sidebarEnd?: React.ReactNode;
23+
/** Flag indicating if the end sidebar is expanded */
24+
isSidebarEndExpanded?: boolean;
25+
/** Content placed at the bottom of the layout */
26+
footer?: React.ReactNode;
27+
/** Flag indicating if the footer is expanded */
28+
isFooterExpanded?: boolean;
29+
/** Content rendered in an optional drawer wrapping the layout */
30+
drawerContent?: React.ReactNode;
31+
/** Additional props passed to the drawer */
32+
drawerProps?: DrawerProps;
33+
/** Light theme background image path of the compass */
34+
backgroundSrcLight?: string;
35+
/** Dark theme background image path of the compass */
36+
backgroundSrcDark?: string;
37+
}
38+
39+
export const Compass: React.FunctionComponent<CompassProps> = ({
40+
className,
41+
header,
42+
isHeaderExpanded = true,
43+
sidebarStart,
44+
isSidebarStartExpanded = true,
45+
main,
46+
sidebarEnd,
47+
isSidebarEndExpanded = true,
48+
footer,
49+
isFooterExpanded = true,
50+
drawerContent,
51+
drawerProps,
52+
backgroundSrcLight,
53+
backgroundSrcDark,
54+
...props
55+
}) => {
56+
const hasDrawer = drawerContent !== undefined;
57+
58+
const backgroundImageStyles: { [key: string]: string } = {};
59+
if (backgroundSrcLight) {
60+
backgroundImageStyles[compassBackgroundImageLight.name] = `url(${backgroundSrcLight})`;
61+
}
62+
if (backgroundSrcDark) {
63+
backgroundImageStyles[compassBackgroundImageDark.name] = `url(${backgroundSrcDark})`;
64+
}
65+
66+
const compassContent = (
67+
<div className={css(styles.compass, className)} {...props} style={{ ...props.style, ...backgroundImageStyles }}>
68+
<div
69+
className={css(styles.compassHeader, isHeaderExpanded && 'pf-m-expanded')}
70+
{...(!isHeaderExpanded && { inert: 'true' })}
71+
>
72+
{header}
73+
</div>
74+
<div
75+
className={css(styles.compassSidebar, styles.modifiers.start, isSidebarStartExpanded && 'pf-m-expanded')}
76+
{...(!isSidebarStartExpanded && { inert: 'true' })}
77+
>
78+
{sidebarStart}
79+
</div>
80+
<div className={css(styles.compassMain)}>{main}</div>
81+
<div
82+
className={css(styles.compassSidebar, styles.modifiers.end, isSidebarEndExpanded && 'pf-m-expanded')}
83+
{...(!isSidebarEndExpanded && { inert: 'true' })}
84+
>
85+
{sidebarEnd}
86+
</div>
87+
<div
88+
className={css(styles.compassFooter, isFooterExpanded && 'pf-m-expanded')}
89+
{...(!isFooterExpanded && { inert: 'true' })}
90+
>
91+
{footer}
92+
</div>
93+
</div>
94+
);
95+
96+
if (hasDrawer) {
97+
return (
98+
<Drawer {...drawerProps}>
99+
<DrawerContent panelContent={drawerContent}>{compassContent}</DrawerContent>
100+
</Drawer>
101+
);
102+
}
103+
104+
return compassContent;
105+
};
106+
107+
Compass.displayName = 'Compass';
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Drawer, DrawerContent, DrawerProps } from '../Drawer';
2+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
3+
import { css } from '@patternfly/react-styles';
4+
5+
interface CompassContentProps extends React.HTMLProps<HTMLDivElement> {
6+
/** Content of the main compass area. Typically one or more CompassPanel components. */
7+
children: React.ReactNode;
8+
/** Additional classes added to the CompassContent */
9+
className?: string;
10+
/** Content rendered in an optional drawer wrapping the CompassContent */
11+
drawerContent?: React.ReactNode;
12+
/** Additional props passed to the drawer */
13+
drawerProps?: DrawerProps;
14+
}
15+
16+
export const CompassContent: React.FunctionComponent<CompassContentProps> = ({
17+
children,
18+
className,
19+
drawerProps,
20+
drawerContent,
21+
...props
22+
}) => {
23+
const hasDrawer = drawerContent !== undefined;
24+
25+
const compassContent = (
26+
<div className={css(styles.compassContent, className)} {...props}>
27+
{children}
28+
</div>
29+
);
30+
31+
if (hasDrawer) {
32+
return (
33+
<Drawer {...drawerProps}>
34+
<DrawerContent panelContent={drawerContent}>{compassContent}</DrawerContent>
35+
</Drawer>
36+
);
37+
}
38+
39+
return compassContent;
40+
};
41+
42+
CompassContent.displayName = 'CompassContent';
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
2+
import { css } from '@patternfly/react-styles';
3+
4+
interface CompassHeaderProps {
5+
/** Content of the logo area */
6+
logo?: React.ReactNode;
7+
/** Content of the navigation area */
8+
nav?: React.ReactNode;
9+
/** Content of the profile area */
10+
profile?: React.ReactNode;
11+
}
12+
13+
export const CompassHeader: React.FunctionComponent<CompassHeaderProps> = ({ logo, nav, profile }) => (
14+
<>
15+
<div className={css(`${styles.compass}__logo`)}>{logo}</div>
16+
<div className={css(styles.compassNav)}>{nav}</div>
17+
<div className={css(styles.compassProfile)}>{profile}</div>
18+
</>
19+
);
20+
21+
CompassHeader.displayName = 'CompassHeader';
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
2+
import { css } from '@patternfly/react-styles';
3+
4+
import compassHeroBackgroundImageLight from '@patternfly/react-tokens/dist/esm/c_compass__hero_BackgroundImage_light';
5+
import compassHeroBackgroundImageDark from '@patternfly/react-tokens/dist/esm/c_compass__hero_BackgroundImage_dark';
6+
import compassHeroGradientStop1Light from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_1_light';
7+
import compassHeroGradientStop2Light from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_2_light';
8+
import compassHeroGradientStop3Light from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_3_light';
9+
import compassHeroGradientStop1Dark from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_1_dark';
10+
import compassHeroGradientStop2Dark from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_2_dark';
11+
import compassHeroGradientStop3Dark from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_3_dark';
12+
13+
interface CompassHeroProps extends Omit<React.HTMLProps<HTMLDivElement>, 'content'> {
14+
/** Content of the hero */
15+
children?: React.ReactNode;
16+
/** Additional classes added to the hero */
17+
className?: string;
18+
/** Light theme background image path of the hero */
19+
backgroundSrcLight?: string;
20+
/** Dark theme background image path of the hero */
21+
backgroundSrcDark?: string;
22+
/** Light theme gradient of the hero */
23+
gradientLight?: {
24+
stop1?: string;
25+
stop2?: string;
26+
stop3?: string;
27+
};
28+
/** Dark theme gradient of the hero */
29+
gradientDark?: {
30+
stop1?: string;
31+
stop2?: string;
32+
stop3?: string;
33+
};
34+
}
35+
36+
export const CompassHero: React.FunctionComponent<CompassHeroProps> = ({
37+
className,
38+
children,
39+
backgroundSrcLight,
40+
backgroundSrcDark,
41+
gradientLight,
42+
gradientDark,
43+
...props
44+
}) => {
45+
const backgroundImageStyles: { [key: string]: string } = {};
46+
if (backgroundSrcLight) {
47+
backgroundImageStyles[compassHeroBackgroundImageLight.name] = `url(${backgroundSrcLight})`;
48+
}
49+
if (backgroundSrcDark) {
50+
backgroundImageStyles[compassHeroBackgroundImageDark.name] = `url(${backgroundSrcDark})`;
51+
}
52+
53+
if (gradientLight) {
54+
if (gradientLight.stop1) {
55+
backgroundImageStyles[compassHeroGradientStop1Light.name] = gradientLight.stop1;
56+
}
57+
if (gradientLight.stop2) {
58+
backgroundImageStyles[compassHeroGradientStop2Light.name] = gradientLight.stop2;
59+
}
60+
if (gradientLight.stop3) {
61+
backgroundImageStyles[compassHeroGradientStop3Light.name] = gradientLight.stop3;
62+
}
63+
}
64+
if (gradientDark) {
65+
if (gradientDark.stop1) {
66+
backgroundImageStyles[compassHeroGradientStop1Dark.name] = gradientDark.stop1;
67+
}
68+
if (gradientDark.stop2) {
69+
backgroundImageStyles[compassHeroGradientStop2Dark.name] = gradientDark.stop2;
70+
}
71+
if (gradientDark.stop3) {
72+
backgroundImageStyles[compassHeroGradientStop3Dark.name] = gradientDark.stop3;
73+
}
74+
}
75+
76+
return (
77+
<div
78+
className={css(styles.compassPanel, styles.compassHero, className)}
79+
style={{ ...props.style, ...backgroundImageStyles }}
80+
{...props}
81+
>
82+
<div className={css(styles.compassHeroBody)}>{children}</div>
83+
</div>
84+
);
85+
};
86+
87+
CompassHero.displayName = 'CompassHero';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Flex, FlexItem } from '../../layouts/Flex';
2+
import { CompassPanel } from './CompassPanel';
3+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
4+
import { css } from '@patternfly/react-styles';
5+
6+
interface CompassMainHeaderProps extends Omit<React.HTMLProps<HTMLDivElement>, 'title'> {
7+
/** Additional classes added to the main header */
8+
className?: string;
9+
/** Styled title. If title or toolbar is provided, the children will be ignored. */
10+
title?: React.ReactNode;
11+
/** Styled toolbar. If title or toolbar is provided, the children will be ignored. */
12+
toolbar?: React.ReactNode;
13+
/** Custom main header content. To opt into a default styling, use the title and toolbar props instead. */
14+
children?: React.ReactNode;
15+
}
16+
17+
export const CompassMainHeader: React.FunctionComponent<CompassMainHeaderProps> = ({
18+
className,
19+
title,
20+
toolbar,
21+
children,
22+
...props
23+
}) => {
24+
const _content =
25+
title !== undefined || toolbar !== undefined ? (
26+
<CompassPanel>
27+
<Flex alignItems={{ default: 'alignItemsCenter' }}>
28+
<FlexItem grow={{ default: 'grow' }}>{title}</FlexItem>
29+
{toolbar && <FlexItem>{toolbar}</FlexItem>}
30+
</Flex>
31+
</CompassPanel>
32+
) : (
33+
children
34+
);
35+
36+
return (
37+
<div className={css(`${styles.compass}__main-header`, className)} {...props}>
38+
{_content}
39+
</div>
40+
);
41+
};
42+
43+
CompassMainHeader.displayName = 'CompassMainHeader';
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
2+
import { css } from '@patternfly/react-styles';
3+
4+
interface CompassMessageBarProps extends React.HTMLProps<HTMLDivElement> {
5+
/** Content of the message bar. Typically a @patternfly/chatbot MessageBar component. */
6+
children?: React.ReactNode;
7+
/** Additional classes added to the message bar */
8+
className?: string;
9+
}
10+
11+
export const CompassMessageBar: React.FunctionComponent<CompassMessageBarProps> = ({
12+
children,
13+
className,
14+
...props
15+
}) => (
16+
<div className={css(styles.compassMessageBar, className)} {...props}>
17+
{children}
18+
</div>
19+
);
20+
21+
CompassMessageBar.displayName = 'CompassMessageBar';
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
2+
import { css } from '@patternfly/react-styles';
3+
4+
interface CompassPanelProps extends React.HTMLProps<HTMLDivElement> {
5+
/** Content of the panel. */
6+
children: React.ReactNode;
7+
/** Additional classes added to the panel. */
8+
className?: string;
9+
/** Indicates the panel should have a pill border radius */
10+
isPill?: boolean;
11+
/** Indicates the panel should expand to fill the available height */
12+
isFullHeight?: boolean;
13+
/** Indicates the panel should scroll its overflow */
14+
isScrollable?: boolean;
15+
/** Indicates the panel should have no border */
16+
hasNoBorder?: boolean;
17+
/** Indicates the panel should have no padding */
18+
hasNoPadding?: boolean;
19+
/** Indicates the panel should have a "thinking" animation */
20+
isThinking?: boolean;
21+
}
22+
23+
export const CompassPanel: React.FunctionComponent<CompassPanelProps> = ({
24+
children,
25+
className,
26+
isPill,
27+
hasNoBorder,
28+
hasNoPadding,
29+
isThinking,
30+
isFullHeight,
31+
isScrollable,
32+
...props
33+
}) => (
34+
<div
35+
className={css(
36+
styles.compassPanel,
37+
isPill && styles.modifiers.pill,
38+
hasNoBorder && styles.modifiers.noBorder,
39+
hasNoPadding && styles.modifiers.noPadding,
40+
isThinking && 'pf-v6-m-thinking',
41+
isFullHeight && styles.modifiers.fullHeight,
42+
isScrollable && styles.modifiers.scrollable,
43+
className
44+
)}
45+
{...props}
46+
>
47+
{children}
48+
</div>
49+
);
50+
51+
CompassPanel.displayName = 'CompassPanel';

0 commit comments

Comments
 (0)