Skip to content

Commit f585594

Browse files
feat: Add Breadcrumb components and related utilities
1 parent 1ba1ccb commit f585594

File tree

14 files changed

+445
-0
lines changed

14 files changed

+445
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export type {
2+
BreadcrumbContextValues,
3+
BreadcrumbProps,
4+
BreadcrumbSlots,
5+
BreadcrumbState
6+
} from '@fluentui/react-breadcrumb';
7+
export {
8+
breadcrumbClassNames,
9+
BreadcrumbProvider,
10+
renderBreadcrumb_unstable as renderBreadcrumb,
11+
useBreadcrumb_unstable as useBreadcrumb,
12+
useBreadcrumbContext_unstable as useBreadcrumbContext,
13+
useBreadcrumbStyles_unstable as useBreadcrumbStyles
14+
} from '@fluentui/react-breadcrumb';
15+
16+
export { Breadcrumb } from './components/Breadcrumb/Breadcrumb';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export { renderBreadcrumbButton_unstable as renderBreadcrumbButton } from '@fluentui/react-breadcrumb';
2+
export type {
3+
BreadcrumbButtonProps,
4+
BreadcrumbButtonSlots,
5+
BreadcrumbButtonState
6+
} from '@fluentui/react-breadcrumb';
7+
8+
export {
9+
breadcrumbButtonClassNames,
10+
useBreadcrumbButtonStyles
11+
} from './components/BreadcrumbButton/useBreadcrumbButtonStyles.styles';
12+
export { BreadcrumbButton } from './components/BreadcrumbButton/BreadcrumbButton';
13+
export { useBreadcrumbButton } from './components/BreadcrumbButton/useBreadcrumbButton';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export { renderBreadcrumbDivider_unstable as renderBreadcrumbDivider } from '@fluentui/react-breadcrumb';
2+
export type {
3+
BreadcrumbDividerProps,
4+
BreadcrumbDividerSlots,
5+
BreadcrumbDividerState
6+
} from '@fluentui/react-breadcrumb';
7+
8+
export { BreadcrumbDivider } from './components/BreadcrumbDivider/BreadcrumbDivider';
9+
export { useBreadcrumbDivider } from './components/BreadcrumbDivider/useBreadcrumbDivider';
10+
export {
11+
breadcrumbDividerClassNames,
12+
useBreadcrumbDividerStyles
13+
} from './components/BreadcrumbDivider/useBreadcrumbDividerStyles.styles';
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export {
2+
renderBreadcrumbItem_unstable as renderBreadcrumbItem,
3+
useBreadcrumbItem_unstable as useBreadcrumbItem
4+
} from '@fluentui/react-breadcrumb';
5+
export type {
6+
BreadcrumbItemProps,
7+
BreadcrumbItemSlots,
8+
BreadcrumbItemState
9+
} from '@fluentui/react-breadcrumb';
10+
11+
export { BreadcrumbItem } from './components/BreadcrumbItem/BreadcrumbItem';
12+
export {
13+
breadcrumbItemClassNames,
14+
useBreadcrumbItemStyles
15+
} from './components/BreadcrumbItem/useBreadcrumbItemStyles.styles';
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Breadcrumb as FluentBreadcrumb } from '@fluentui/react-breadcrumb';
2+
import type { BreadcrumbProps } from '@fluentui/react-breadcrumb';
3+
import type { ForwardRefComponent } from '@fluentui/react-utilities';
4+
5+
/**
6+
* Breadcrumb is a to navigate through a hierarchy.
7+
*
8+
* @example
9+
* ```tsx
10+
* <Breadcrumb>
11+
* <BreadcrumbItem>
12+
* <BreadcrumbButton href='#'>Home</BreadcrumbButton>
13+
* </BreadcrumbItem>
14+
* <BreadcrumbDivider />
15+
* <BreadcrumbItem>
16+
* <BreadcrumbButton href='#/contact-us' current>Contact us</BreadcrumbButton>
17+
* </BreadcrumbItem>
18+
* </Breadcrumb>
19+
* ```
20+
* @alpha
21+
*/
22+
export const Breadcrumb: ForwardRefComponent<BreadcrumbProps> = FluentBreadcrumb;
23+
24+
Breadcrumb.displayName = 'Breadcrumb';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { forwardRef } from 'react';
2+
import { renderBreadcrumbButton_unstable } from '@fluentui/react-breadcrumb';
3+
import type { BreadcrumbButtonProps } from '@fluentui/react-breadcrumb';
4+
import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
5+
import type { ForwardRefComponent } from '@fluentui/react-utilities';
6+
7+
import { useBreadcrumbButton } from './useBreadcrumbButton';
8+
import { useBreadcrumbButtonStyles } from './useBreadcrumbButtonStyles.styles';
9+
10+
/**
11+
* BreadcrumbButton is a button to navigate through a hierarchy.
12+
*
13+
* @example
14+
* ```tsx
15+
* <BreadcrumbItem>
16+
* <BreadcrumbButton href='#/contact-us' current>Contact us</BreadcrumbButton>
17+
* </BreadcrumbItem>
18+
* ```
19+
*
20+
* @alpha
21+
*/
22+
export const BreadcrumbButton: ForwardRefComponent<BreadcrumbButtonProps> = forwardRef((props, ref) => {
23+
const state = useBreadcrumbButton(props, ref);
24+
25+
useBreadcrumbButtonStyles(state);
26+
useCustomStyleHook_unstable('useBreadcrumbButtonStyles_unstable')(state);
27+
28+
return renderBreadcrumbButton_unstable(state);
29+
});
30+
31+
BreadcrumbButton.displayName = 'BreadcrumbButton';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useBreadcrumbButton_unstable } from '@fluentui/react-breadcrumb';
2+
import type { BreadcrumbButtonProps, BreadcrumbButtonState } from '@fluentui/react-breadcrumb';
3+
import { useButton } from '../../../react-button/Button';
4+
5+
6+
/**
7+
* Create the state required to style and render the BreadcrumbButton.
8+
* @param props - Props of the BreadcrumbButton
9+
* @param ref - Reference to the root button
10+
* @returns The BreadcrumbButton state object
11+
* @alpha
12+
*/
13+
export const useBreadcrumbButton = (
14+
props: BreadcrumbButtonProps,
15+
ref: React.Ref<HTMLButtonElement | HTMLAnchorElement>
16+
): BreadcrumbButtonState => {
17+
const { root, ...baseState } = useBreadcrumbButton_unstable(props, ref);
18+
const buttonState = useButton(
19+
{
20+
...baseState,
21+
...root
22+
},
23+
ref
24+
);
25+
26+
return {
27+
...buttonState,
28+
appearance: baseState.appearance,
29+
current: baseState.current
30+
};
31+
};
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { useBreadcrumbButtonStyles_unstable } from '@fluentui/react-breadcrumb';
2+
import type { BreadcrumbButtonSlots, BreadcrumbButtonState } from '@fluentui/react-breadcrumb';
3+
import type { SlotClassNames } from '@fluentui/react-utilities';
4+
import { makeStyles, mergeClasses } from '@griffel/react';
5+
import { tokens, typographyStyles } from '../../../tokens/index';
6+
import { buttonClassNames, useButtonStyles } from '../../../react-button/Button';
7+
8+
/**
9+
* CSS class names for the BreadcrumbButton component slots.
10+
* @alpha
11+
*/
12+
export const breadcrumbButtonClassNames: SlotClassNames<BreadcrumbButtonSlots> = {
13+
root: 'fui-BreadcrumbButton',
14+
icon: 'fui-BreadcrumbButton__icon'
15+
};
16+
17+
const fluentButtonIcon = 'fui-Button__icon';
18+
const iconFilledClassName = 'fui-Icon-filled';
19+
const iconRegularClassName = 'fui-Icon-regular';
20+
21+
const currentStateStyles = {
22+
color: tokens.colorNeutralForeground3,
23+
backgroundColor: tokens.colorTransparentBackground,
24+
[`& .${iconFilledClassName}`]: { display: 'none' },
25+
[`& .${iconRegularClassName}`]: { display: 'inline' },
26+
[`& .${fluentButtonIcon}`]: { color: 'unset' }
27+
};
28+
29+
const useStyles = makeStyles({
30+
root: { height: 'unset' },
31+
small: {},
32+
medium: {},
33+
large: {
34+
...typographyStyles.subtitle1,
35+
fontWeight: tokens.fontSizeBase400,
36+
paddingTop: tokens.spacingVerticalSNudge,
37+
paddingBottom: tokens.spacingVerticalSNudge
38+
},
39+
current: {
40+
fontWeight: tokens.fontWeightSemibold,
41+
':hover': currentStateStyles,
42+
':active:hover': {
43+
...currentStateStyles,
44+
[`& .${buttonClassNames.icon}`]: { color: 'unset' }
45+
}
46+
}
47+
});
48+
49+
const useIconOnlyStyles = makeStyles({
50+
small: {},
51+
medium: {},
52+
large: { paddingTop: tokens.spacingVerticalMNudge, paddingBottom: tokens.spacingVerticalMNudge },
53+
icon: { marginRight: tokens.spacingHorizontalNone }
54+
});
55+
56+
/**
57+
* Apply styling to the BreadcrumbButton based on the state.
58+
* @param state - The BreadcrumbButton state object
59+
* @returns The styled BreadcrumbButton state
60+
* @alpha
61+
*/
62+
export const useBreadcrumbButtonStyles = (state: BreadcrumbButtonState): BreadcrumbButtonState => {
63+
const styles = useStyles();
64+
const iconOnlyStyles = useIconOnlyStyles();
65+
const { current, iconOnly, size } = state;
66+
67+
state.root.className = mergeClasses(
68+
breadcrumbButtonClassNames.root,
69+
styles.root,
70+
styles[size],
71+
iconOnly && iconOnlyStyles[size],
72+
current && styles.current,
73+
state.root.className
74+
);
75+
76+
if (state.icon) {
77+
state.icon.className = mergeClasses(
78+
breadcrumbButtonClassNames.icon,
79+
iconOnly && iconOnlyStyles.icon,
80+
state.icon.className
81+
);
82+
}
83+
84+
useButtonStyles(state);
85+
useBreadcrumbButtonStyles_unstable(state);
86+
87+
return state;
88+
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { forwardRef } from 'react';
2+
import { renderBreadcrumbDivider_unstable } from '@fluentui/react-breadcrumb';
3+
import type { BreadcrumbDividerProps } from '@fluentui/react-breadcrumb';
4+
import type { ForwardRefComponent } from '@fluentui/react-utilities';
5+
import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts';
6+
7+
import { useBreadcrumbDivider } from './useBreadcrumbDivider';
8+
import { useBreadcrumbDividerStyles } from './useBreadcrumbDividerStyles.styles';
9+
10+
/**
11+
* A divider to separate each `BreadcrumbItem`.
12+
*
13+
* @example
14+
* ```tsx
15+
* <Breadcrumb>
16+
* <BreadcrumbItem>
17+
* <BreadcrumbButton href='#'>Home</BreadcrumbButton>
18+
* </BreadcrumbItem>
19+
* <BreadcrumbDivider />
20+
* <BreadcrumbItem>
21+
* <BreadcrumbButton href='#/contact-us' current>Contact us</BreadcrumbButton>
22+
* </BreadcrumbItem>
23+
* </Breadcrumb>
24+
* ```
25+
*
26+
* @alpha
27+
*/
28+
export const BreadcrumbDivider: ForwardRefComponent<BreadcrumbDividerProps> = forwardRef((props, ref) => {
29+
const state = useBreadcrumbDivider(props, ref);
30+
31+
useBreadcrumbDividerStyles(state);
32+
useCustomStyleHook_unstable('useBreadcrumbDividerStyles_unstable')(state);
33+
34+
return renderBreadcrumbDivider_unstable(state);
35+
});
36+
37+
BreadcrumbDivider.displayName = 'BreadcrumbDivider';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useBreadcrumbDivider_unstable } from '@fluentui/react-breadcrumb';
2+
import type { BreadcrumbDividerProps, BreadcrumbDividerState } from '@fluentui/react-breadcrumb';
3+
4+
/**
5+
* Create the state required to style and render the BreadcrumbDivider.
6+
* @param props - Props of the BreadcrumbDivider
7+
* @param ref - Reference to the root element
8+
* @returns The BreadcrumbDivider state object
9+
* @alpha
10+
*/
11+
export const useBreadcrumbDivider = (
12+
props: BreadcrumbDividerProps,
13+
ref: React.Ref<HTMLLIElement>
14+
): BreadcrumbDividerState => ({ ...useBreadcrumbDivider_unstable(props, ref), size: 'medium' });

0 commit comments

Comments
 (0)