Skip to content

Commit 45a3998

Browse files
committed
feat(react-headless-components-preview): add Toolbar component
1 parent fe3a564 commit 45a3998

33 files changed

+728
-0
lines changed

packages/react-components/react-headless-components-preview/library/etc/react-headless-components-preview.api.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,19 @@ import { TextareaBaseState } from '@fluentui/react-textarea';
121121
import type { TextareaSlots as TextareaSlots_2 } from '@fluentui/react-textarea';
122122
import type { ToggleButtonBaseProps } from '@fluentui/react-button';
123123
import type { ToggleButtonBaseState } from '@fluentui/react-button';
124+
import type { ToolbarButtonProps as ToolbarButtonProps_2 } from '@fluentui/react-toolbar';
125+
import type { ToolbarButtonState as ToolbarButtonState_2 } from '@fluentui/react-toolbar';
126+
import { ToolbarContextValue } from '@fluentui/react-toolbar';
127+
import { ToolbarContextValues as ToolbarContextValues_2 } from '@fluentui/react-toolbar';
128+
import type { ToolbarDividerProps as ToolbarDividerProps_2 } from '@fluentui/react-toolbar';
129+
import type { ToolbarDividerState as ToolbarDividerState_2 } from '@fluentui/react-toolbar';
130+
import type { ToolbarGroupProps as ToolbarGroupProps_2 } from '@fluentui/react-toolbar';
131+
import { ToolbarGroupState as ToolbarGroupState_2 } from '@fluentui/react-toolbar';
132+
import type { ToolbarProps as ToolbarProps_2 } from '@fluentui/react-toolbar';
133+
import type { ToolbarRadioGroupProps as ToolbarRadioGroupProps_2 } from '@fluentui/react-toolbar';
134+
import type { ToolbarRadioGroupState as ToolbarRadioGroupState_2 } from '@fluentui/react-toolbar';
135+
import type { ToolbarSlots as ToolbarSlots_2 } from '@fluentui/react-toolbar';
136+
import { ToolbarState as ToolbarState_2 } from '@fluentui/react-toolbar';
124137

125138
// @public
126139
export const Accordion: ForwardRefComponent<AccordionProps>;
@@ -563,6 +576,21 @@ export const renderTextarea: (state: TextareaBaseState) => JSXElement;
563576
// @public
564577
export const renderToggleButton: (state: ButtonBaseState) => JSXElement;
565578

579+
// @public
580+
export const renderToolbar: (state: ToolbarState_2, contextValues: ToolbarContextValues_2) => JSXElement;
581+
582+
// @public
583+
export const renderToolbarButton: (state: ButtonBaseState) => JSXElement;
584+
585+
// @public
586+
export const renderToolbarDivider: (state: DividerBaseState) => JSXElement;
587+
588+
// @public
589+
export const renderToolbarGroup: (state: ToolbarGroupState_2) => JSXElement;
590+
591+
// @public
592+
export const renderToolbarRadioGroup: (state: ToolbarGroupState_2) => JSXElement;
593+
566594
// @public
567595
export const SearchBox: ForwardRefComponent<SearchBoxProps>;
568596

@@ -756,6 +784,82 @@ export type ToggleButtonState = ToggleButtonBaseState & {
756784
};
757785
};
758786

787+
// @public
788+
export const Toolbar: ForwardRefComponent<ToolbarProps>;
789+
790+
// @public
791+
export const ToolbarButton: ForwardRefComponent<ToolbarButtonProps>;
792+
793+
// @public (undocumented)
794+
export type ToolbarButtonProps = ToolbarButtonProps_2;
795+
796+
// @public (undocumented)
797+
export type ToolbarButtonState = ToolbarButtonState_2 & {
798+
root: {
799+
'data-vertical'?: string;
800+
'data-disabled'?: string;
801+
'data-disabled-focusable'?: string;
802+
'data-icon-only'?: string;
803+
};
804+
};
805+
806+
// @public (undocumented)
807+
export type ToolbarContextValues = ToolbarContextValues_2;
808+
809+
// @public
810+
export const ToolbarDivider: ForwardRefComponent<ToolbarDividerProps>;
811+
812+
// @public (undocumented)
813+
export type ToolbarDividerProps = ToolbarDividerProps_2;
814+
815+
// @public (undocumented)
816+
export type ToolbarDividerState = ToolbarDividerState_2 & {
817+
root: {
818+
'data-vertical'?: string;
819+
};
820+
};
821+
822+
// @public
823+
export const ToolbarGroup: ForwardRefComponent<ToolbarGroupProps>;
824+
825+
// @public (undocumented)
826+
export type ToolbarGroupProps = ToolbarGroupProps_2;
827+
828+
// @public (undocumented)
829+
export type ToolbarGroupState = ToolbarGroupState_2 & {
830+
root: {
831+
'data-vertical'?: string;
832+
};
833+
};
834+
835+
// @public (undocumented)
836+
export type ToolbarProps = ToolbarProps_2;
837+
838+
// @public
839+
export const ToolbarRadioGroup: ForwardRefComponent<ToolbarRadioGroupProps>;
840+
841+
// @public (undocumented)
842+
export type ToolbarRadioGroupProps = ToolbarRadioGroupProps_2;
843+
844+
// @public (undocumented)
845+
export type ToolbarRadioGroupState = ToolbarRadioGroupState_2 & {
846+
vertical?: boolean;
847+
root: {
848+
'data-vertical'?: string;
849+
};
850+
};
851+
852+
// @public (undocumented)
853+
export type ToolbarSlots = ToolbarSlots_2;
854+
855+
// @public (undocumented)
856+
export type ToolbarState = ToolbarState_2 & {
857+
root: {
858+
'data-vertical'?: string;
859+
'data-size'?: string;
860+
};
861+
};
862+
759863
// @public
760864
export const useAccordion: (props: AccordionProps, ref: React_2.Ref<HTMLElement>) => AccordionState;
761865

@@ -870,6 +974,27 @@ export const useTextarea: (props: TextareaProps, ref: React_2.Ref<HTMLTextAreaEl
870974
// @public
871975
export const useToggleButton: (props: ToggleButtonProps, ref: React_2.Ref<HTMLButtonElement | HTMLAnchorElement>) => ToggleButtonState;
872976

977+
// @public
978+
export const useToolbar: (props: ToolbarProps, ref: React_2.Ref<HTMLElement>) => ToolbarState;
979+
980+
// @public
981+
export const useToolbarButton: (props: ToolbarButtonProps, ref: React_2.Ref<HTMLButtonElement | HTMLAnchorElement>) => ToolbarButtonState;
982+
983+
// @public
984+
export const useToolbarContext: <T>(selector: ContextSelector<ToolbarContextValue, T>) => T;
985+
986+
// @public
987+
export const useToolbarContextValues: (state: ToolbarState) => ToolbarContextValues;
988+
989+
// @public
990+
export const useToolbarDivider: (props: ToolbarDividerProps, ref: React_2.Ref<HTMLElement>) => ToolbarDividerState;
991+
992+
// @public
993+
export const useToolbarGroup: (props: ToolbarGroupProps, ref: React_2.Ref<HTMLDivElement>) => ToolbarGroupState;
994+
995+
// @public
996+
export const useToolbarRadioGroup: (props: ToolbarRadioGroupProps, ref: React_2.Ref<HTMLDivElement>) => ToolbarRadioGroupState;
997+
873998
// (No @packageDocumentation comment for this package)
874999

8751000
```

packages/react-components/react-headless-components-preview/library/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,15 @@
4747
"@fluentui/react-tabs": "^9.11.2",
4848
"@fluentui/react-tags": "^9.7.19",
4949
"@fluentui/react-textarea": "^9.7.0",
50+
"@fluentui/react-toolbar": "^9.7.7",
5051
"@fluentui/react-utilities": "^9.26.2",
5152
"@swc/helpers": "^0.5.1"
5253
},
54+
"devDependencies": {
55+
"@microsoft/focusgroup-polyfill": "^1.2.1"
56+
},
5357
"peerDependencies": {
58+
"@microsoft/focusgroup-polyfill": "^1.2.1",
5459
"@types/react": ">=16.14.0 <20.0.0",
5560
"@types/react-dom": ">=16.9.0 <20.0.0",
5661
"react": ">=16.14.0 <20.0.0",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export { Toolbar, renderToolbar, useToolbar, useToolbarContext, useToolbarContextValues } from './components/Toolbar';
2+
export type { ToolbarSlots, ToolbarProps, ToolbarState, ToolbarContextValues } from './components/Toolbar';
3+
4+
export { ToolbarButton, renderToolbarButton, useToolbarButton } from './components/Toolbar';
5+
export type { ToolbarButtonProps, ToolbarButtonState } from './components/Toolbar';
6+
7+
export { ToolbarDivider, renderToolbarDivider, useToolbarDivider } from './components/Toolbar';
8+
export type { ToolbarDividerProps, ToolbarDividerState } from './components/Toolbar';
9+
10+
export { ToolbarGroup, renderToolbarGroup, useToolbarGroup } from './components/Toolbar';
11+
export type { ToolbarGroupProps, ToolbarGroupState } from './components/Toolbar';
12+
13+
export { ToolbarRadioGroup, renderToolbarRadioGroup, useToolbarRadioGroup } from './components/Toolbar';
14+
export type { ToolbarRadioGroupProps, ToolbarRadioGroupState } from './components/Toolbar';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use client';
2+
3+
import * as React from 'react';
4+
import type { ForwardRefComponent } from '@fluentui/react-utilities';
5+
import type { ToolbarProps } from './Toolbar.types';
6+
import { useToolbar, useToolbarContextValues } from './useToolbar';
7+
import { renderToolbar } from './renderToolbar';
8+
9+
/**
10+
* A toolbar component that provides a container for grouping a set of controls such as buttons and menu items.
11+
*/
12+
export const Toolbar: ForwardRefComponent<ToolbarProps> = React.forwardRef((props, ref) => {
13+
const state = useToolbar(props, ref);
14+
const contextValues = useToolbarContextValues(state);
15+
16+
return renderToolbar(state, contextValues);
17+
});
18+
19+
Toolbar.displayName = 'Toolbar';
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type {
2+
ToolbarSlots as ToolbarBaseSlots,
3+
ToolbarProps as ToolbarBaseProps,
4+
ToolbarContextValues as ToolbarBaseContextValues,
5+
ToolbarState as ToolbarBaseState,
6+
} from '@fluentui/react-toolbar';
7+
8+
export type ToolbarSlots = ToolbarBaseSlots;
9+
10+
export type ToolbarProps = ToolbarBaseProps;
11+
12+
export type ToolbarState = ToolbarBaseState & {
13+
root: {
14+
/**
15+
* Data attribute set when the toolbar is vertically oriented.
16+
*/
17+
'data-vertical'?: string;
18+
/**
19+
* Data attribute reflecting the current size of the toolbar. Value is 'small', 'medium', or 'large'.
20+
*/
21+
'data-size'?: string;
22+
};
23+
};
24+
25+
export type ToolbarContextValues = ToolbarBaseContextValues;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use client';
2+
3+
import * as React from 'react';
4+
import type { ForwardRefComponent } from '@fluentui/react-utilities';
5+
import type { ToolbarButtonProps } from './ToolbarButton.types';
6+
import { useToolbarButton } from './useToolbarButton';
7+
import { renderToolbarButton } from './renderToolbarButton';
8+
9+
/**
10+
* A button component designed to be used inside a Toolbar, inheriting toolbar context such as size.
11+
*/
12+
export const ToolbarButton: ForwardRefComponent<ToolbarButtonProps> = React.forwardRef((props, ref) => {
13+
const state = useToolbarButton(props, ref);
14+
15+
return renderToolbarButton(state);
16+
// Casting is required due to lack of distributive union to support unions on @types/react
17+
}) as ForwardRefComponent<ToolbarButtonProps>;
18+
19+
ToolbarButton.displayName = 'ToolbarButton';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type {
2+
ToolbarButtonProps as ToolbarButtonBaseProps,
3+
ToolbarButtonState as ToolbarButtonBaseState,
4+
} from '@fluentui/react-toolbar';
5+
6+
export type ToolbarButtonProps = ToolbarButtonBaseProps;
7+
8+
export type ToolbarButtonState = ToolbarButtonBaseState & {
9+
root: {
10+
/**
11+
* Data attribute set when the button is in a vertically oriented toolbar.
12+
*/
13+
'data-vertical'?: string;
14+
15+
/**
16+
* Data attribute set when the button is disabled.
17+
*/
18+
'data-disabled'?: string;
19+
20+
/**
21+
* Data attribute set when the button is disabled but still focusable.
22+
*/
23+
'data-disabled-focusable'?: string;
24+
25+
/**
26+
* Data attribute set when the button renders only an icon.
27+
*/
28+
'data-icon-only'?: string;
29+
};
30+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export { ToolbarButton } from './ToolbarButton';
2+
export { renderToolbarButton } from './renderToolbarButton';
3+
export { useToolbarButton } from './useToolbarButton';
4+
export type { ToolbarButtonProps, ToolbarButtonState } from './ToolbarButton.types';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { renderButton_unstable } from '@fluentui/react-button';
2+
3+
/**
4+
* Renders the final JSX of the ToolbarButton component, given the state.
5+
*/
6+
export const renderToolbarButton = renderButton_unstable;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use client';
2+
3+
import type * as React from 'react';
4+
import { useToolbarButton_unstable } from '@fluentui/react-toolbar';
5+
6+
import type { ToolbarButtonProps, ToolbarButtonState } from './ToolbarButton.types';
7+
import { stringifyDataAttribute } from '../../../utils';
8+
9+
/**
10+
* Returns the state for a ToolbarButton component, given its props and ref.
11+
* The returned state can be modified with hooks before being passed to `renderToolbarButton`.
12+
*/
13+
export const useToolbarButton = (
14+
props: ToolbarButtonProps,
15+
ref: React.Ref<HTMLButtonElement | HTMLAnchorElement>,
16+
): ToolbarButtonState => {
17+
'use no memo';
18+
19+
const state: ToolbarButtonState = useToolbarButton_unstable(props, ref);
20+
21+
// Set data attributes for vertical, disabled, disabledFocusable, and iconOnly states to simplify styling.
22+
state.root['data-vertical'] = stringifyDataAttribute(state.vertical);
23+
state.root['data-disabled'] = stringifyDataAttribute(state.disabled);
24+
state.root['data-disabled-focusable'] = stringifyDataAttribute(state.disabledFocusable);
25+
state.root['data-icon-only'] = stringifyDataAttribute(state.iconOnly);
26+
27+
return state;
28+
};

0 commit comments

Comments
 (0)