Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: introduce headless style hooks for button components",
"packageName": "@fluentui/react-button",
"email": "dmytrokirpa@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: implement headless style hook for Divider component",
"packageName": "@fluentui/react-divider",
"email": "dmytrokirpa@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use client';

import type { SlotClassNames } from '@fluentui/react-utilities';
import type { ButtonSlots, ButtonState } from './Button.types';

export const buttonClassNames: SlotClassNames<ButtonSlots> = {
root: 'fui-Button',
icon: 'fui-Button__icon',
};

/**
* Attaches only semantic slot class names and state modifiers
*/
export const useButtonStyles_unstable = (state: ButtonState): ButtonState => {
'use no memo';

const { appearance, disabled, disabledFocusable, icon, iconOnly, iconPosition, shape, size } = state;

state.root.className = [
buttonClassNames.root,

// Appearance
appearance && `${buttonClassNames.root}--${appearance}`,

// Size
`${buttonClassNames.root}--${size}`,

// Shape
`${buttonClassNames.root}--${shape}`,

// Disabled styles
disabled && `${buttonClassNames.root}--disabled`,
disabledFocusable && `${buttonClassNames.root}--disabledFocusable`,

// Icon styles
icon && iconPosition === 'before' && `${buttonClassNames.root}--iconBefore`,
icon && iconPosition === 'after' && `${buttonClassNames.root}--iconAfter`,
iconOnly && `${buttonClassNames.root}--iconOnly`,

// User provided class name
state.root.className,
]
.filter(Boolean)
.join(' ');

if (state.icon) {
state.icon.className = [buttonClassNames.icon, state.icon.className].filter(Boolean).join(' ');
}

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use client';

import type { SlotClassNames } from '@fluentui/react-utilities';
import type { CompoundButtonSlots, CompoundButtonState } from './CompoundButton.types';

// Re-export the same slot class names mapping used by the griffel styles file
export const compoundButtonClassNames: SlotClassNames<CompoundButtonSlots> = {
root: 'fui-CompoundButton',
icon: 'fui-CompoundButton__icon',
contentContainer: 'fui-CompoundButton__contentContainer',
secondaryContent: 'fui-CompoundButton__secondaryContent',
};

/**
* Attaches only semantic slot class names and state modifiers
*/
export const useCompoundButtonStyles_unstable = (state: CompoundButtonState): CompoundButtonState => {
'use no memo';

const { appearance, disabled, disabledFocusable, icon, iconOnly, iconPosition, shape, size } = state;

state.root.className = [
compoundButtonClassNames.root,

// Appearance
appearance && `${compoundButtonClassNames.root}--${appearance}`,

// Size
size && `${compoundButtonClassNames.root}--${size}`,

// Shape
shape && `${compoundButtonClassNames.root}--${shape}`,

// Disabled styles
disabled && `${compoundButtonClassNames.root}--disabled`,
disabledFocusable && `${compoundButtonClassNames.root}--disabledFocusable`,

// Icon styles
icon && iconPosition === 'before' && `${compoundButtonClassNames.root}--iconBefore`,
icon && iconPosition === 'after' && `${compoundButtonClassNames.root}--iconAfter`,
icon && iconOnly && `${compoundButtonClassNames.root}--iconOnly`,

// User provided class name
state.root.className,
]
.filter(Boolean)
.join(' ');

if (state.icon) {
state.icon.className = [compoundButtonClassNames.icon, state.icon.className].filter(Boolean).join(' ');
}

state.contentContainer.className = [compoundButtonClassNames.contentContainer, state.contentContainer.className]
.filter(Boolean)
.join(' ');

if (state.secondaryContent) {
state.secondaryContent.className = [compoundButtonClassNames.secondaryContent, state.secondaryContent.className]
.filter(Boolean)
.join(' ');
}

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use client';

import type { SlotClassNames } from '@fluentui/react-utilities';
import type { MenuButtonSlots, MenuButtonState } from './MenuButton.types';

export const menuButtonClassNames: SlotClassNames<MenuButtonSlots> = {
root: 'fui-MenuButton',
icon: 'fui-MenuButton__icon',
menuIcon: 'fui-MenuButton__menuIcon',
};

/**
* Attaches only semantic slot class names and state modifiers
*/
export const useMenuButtonStyles_unstable = (state: MenuButtonState): MenuButtonState => {
'use no memo';

const { appearance, disabled, disabledFocusable, shape, size, icon, iconOnly } = state;
const expanded = !!state.root['aria-expanded'];

state.root.className = [
menuButtonClassNames.root,

// Appearance
appearance && `${menuButtonClassNames.root}--${appearance}`,

// Size
size && `${menuButtonClassNames.root}--${size}`,

// Shape
shape && `${menuButtonClassNames.root}--${shape}`,

// Disabled styles
disabled && `${menuButtonClassNames.root}--disabled`,
disabledFocusable && `${menuButtonClassNames.root}--disabledFocusable`,

// Expanded
expanded && `${menuButtonClassNames.root}--expanded`,

// Icons
icon && iconOnly && `${menuButtonClassNames.root}--iconOnly`,

// User provided class name
state.root.className,
]
.filter(Boolean)
.join(' ');

if (state.icon) {
state.icon.className = [menuButtonClassNames.icon, state.icon.className].filter(Boolean).join(' ');
}

if (state.menuIcon) {
state.menuIcon.className = [menuButtonClassNames.menuIcon, state.menuIcon.className].filter(Boolean).join(' ');
}

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use client';

import type { SlotClassNames } from '@fluentui/react-utilities';
import type { SplitButtonSlots, SplitButtonState } from './SplitButton.types';

export const splitButtonClassNames: SlotClassNames<SplitButtonSlots> = {
root: 'fui-SplitButton',
menuButton: 'fui-SplitButton__menuButton',
primaryActionButton: 'fui-SplitButton__primaryActionButton',
};

/**
* Attaches only semantic slot class names and state modifiers
*/
export const useSplitButtonStyles_unstable = (state: SplitButtonState): SplitButtonState => {
'use no memo';

const { appearance, disabled, disabledFocusable, shape, size } = state;

state.root.className = [
splitButtonClassNames.root,

// Appearance
appearance && `${splitButtonClassNames.root}--${appearance}`,

// Size
size && `${splitButtonClassNames.root}--${size}`,

// Shape
shape && `${splitButtonClassNames.root}--${shape}`,

// Disabled styles
disabled && `${splitButtonClassNames.root}--disabled`,
disabledFocusable && !disabled && `${splitButtonClassNames.root}--disabledFocusable`,

// User provided class name
state.root.className,
]
.filter(Boolean)
.join(' ');

if (state.primaryActionButton) {
state.primaryActionButton.className = [
splitButtonClassNames.primaryActionButton,
state.primaryActionButton.className,
]
.filter(Boolean)
.join(' ');
}

if (state.menuButton) {
state.menuButton.className = [splitButtonClassNames.menuButton, state.menuButton.className]
.filter(Boolean)
.join(' ');
}

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use client';

import type { SlotClassNames } from '@fluentui/react-utilities';
import type { ButtonSlots } from '../Button/Button.types';
import type { ToggleButtonState } from './ToggleButton.types';

export const toggleButtonClassNames: SlotClassNames<ButtonSlots> = {
root: 'fui-ToggleButton',
icon: 'fui-ToggleButton__icon',
};

/**
* Attaches only semantic slot class names and state modifiers
*/
export const useToggleButtonStyles_unstable = (state: ToggleButtonState): ToggleButtonState => {
'use no memo';

const { appearance, disabled, disabledFocusable, shape, size, checked, iconOnly } = state;

state.root.className = [
toggleButtonClassNames.root,

// Appearance
appearance && `${toggleButtonClassNames.root}--${appearance}`,

// Size
size && `${toggleButtonClassNames.root}--${size}`,

// Shape
shape && `${toggleButtonClassNames.root}--${shape}`,

// Checked
checked && `${toggleButtonClassNames.root}--checked`,

// Icons
iconOnly && `${toggleButtonClassNames.root}--iconOnly`,

// Disabled
disabled && `${toggleButtonClassNames.root}--disabled`,
disabledFocusable && `${toggleButtonClassNames.root}--disabledFocusable`,

// User provided class name
state.root.className,
]
.filter(Boolean)
.join(' ');

if (state.icon) {
state.icon.className = [toggleButtonClassNames.icon, state.icon.className].filter(Boolean).join(' ');
}

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use client';

import { DividerSlots, DividerState } from './Divider.types';
import type { SlotClassNames } from '@fluentui/react-utilities';

export const dividerClassNames: SlotClassNames<DividerSlots> = {
root: 'fui-Divider',
wrapper: 'fui-Divider__wrapper',
};

/**
* Attaches only semantic slot class names and state modifiers
*/
export const useDividerStyles_unstable = (state: DividerState): DividerState => {
'use no memo';

const { alignContent, appearance, inset, vertical } = state;

state.root.className = [
dividerClassNames.root,

// Alignment
`${dividerClassNames}--align-${alignContent}`,

// Appearance
`${dividerClassNames}--${appearance}`,

// Orientation
vertical ? `${dividerClassNames}--vertical` : `${dividerClassNames}--horizontal`,

// Inset
inset && `${dividerClassNames}--inset`,

// User provided class name
state.root.className,
]
.filter(Boolean)
.join(' ');

if (state.wrapper) {
state.wrapper.className = [dividerClassNames.wrapper, state.wrapper.className].filter(Boolean).join(' ');
}

return state;
};