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": "patch",
"comment": "fix: createFocusOutlineStyle cannot work with CSS variables",
"packageName": "@fluentui/react-card",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: createFocusOutlineStyle cannot work with CSS variables",
"packageName": "@fluentui/react-tabster",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { shorthands, makeStyles, mergeClasses } from '@griffel/react';
import { tokens } from '@fluentui/react-theme';
import type { SlotClassNames } from '@fluentui/react-utilities';
import { createFocusOutlineStyle } from '@fluentui/react-tabster';
import { FocusOutlineStyleOptions, createFocusOutlineStyle } from '@fluentui/react-tabster';

import { cardPreviewClassNames } from '../CardPreview/useCardPreviewStyles.styles';
import { cardHeaderClassNames } from '../CardHeader/useCardHeaderStyles.styles';
import { cardFooterClassNames } from '../CardFooter/useCardFooterStyles.styles';
import type { CardSlots, CardState } from './Card.types';
import * as React from 'react';

/**
* Static CSS class names used internally for the component slots.
Expand All @@ -25,9 +26,10 @@ export const cardCSSVars = {
cardBorderRadiusVar: '--fui-Card--border-radius',
};

const focusOutlineStyle = {
const focusOutlineStyle: Partial<FocusOutlineStyleOptions> = {
outlineRadius: `var(${cardCSSVars.cardBorderRadiusVar})`,
outlineWidth: tokens.strokeWidthThick,
outlineOffset: '-2px', // FIXME: tokens.strokeWidthThick causes some weird bugs
};

const useStyles = makeStyles({
Expand Down Expand Up @@ -65,7 +67,9 @@ const useStyles = makeStyles({
[`> :not(.${cardPreviewClassNames.root}):not(.${cardHeaderClassNames.root}):not(.${cardFooterClassNames.root})`]: {
flexGrow: 1,
},
},

focused: {
...createFocusOutlineStyle({
style: focusOutlineStyle,
selector: 'focus',
Expand Down Expand Up @@ -389,6 +393,18 @@ export const useCardStyles_unstable = (state: CardState): CardState => {

const isSelectableOrInteractive = state.interactive || state.selectable;

const focusedClassName = React.useMemo(() => {
if (state.selectable) {
if (state.selectFocused) {
return styles.selectableFocused;
}

return '';
}

return styles.focused;
}, [state.selectFocused, state.selectable, styles.focused, styles.selectableFocused]);

state.root.className = mergeClasses(
cardClassNames.root,
styles.root,
Expand All @@ -397,7 +413,8 @@ export const useCardStyles_unstable = (state: CardState): CardState => {
appearanceMap[state.appearance],
isSelectableOrInteractive && interactiveMap[state.appearance],
state.selected && selectedMap[state.appearance],
state.selectFocused && styles.selectableFocused,
// Focus overrides
focusedClassName,
// High contrast overrides
state.selected && styles.highContrastSelected,
isSelectableOrInteractive && styles.highContrastInteractive,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,26 @@ export interface CreateFocusOutlineStyleOptions extends Omit<CreateCustomFocusIn
enableOutline?: boolean;
}

/**
* Get the position of the focus outline
*
* @param options - Configures the style of the focus outline
* @param position - The position of the focus outline
* @returns CSS value for the position of the focus outline
*/
function getOutlinePosition(
{ outlineWidth, outlineOffset }: FocusOutlineStyleOptions,
position: 'top' | 'bottom' | 'left' | 'right',
) {
const offsetValue = (outlineOffset as FocusOutlineOffset)?.[position] || outlineOffset;

if (!outlineOffset) {
return `calc(${outlineWidth} * -1)`;
}

return `calc(0px - ${outlineWidth} - ${offsetValue})`;
}

/**
* NOTE: the element with the focus outline needs to have `position: relative` so that the
* pseudo element can be properly positioned.
Expand All @@ -35,12 +55,7 @@ export interface CreateFocusOutlineStyleOptions extends Omit<CreateCustomFocusIn
* @returns focus outline styles object
*/
const getFocusOutlineStyles = (options: FocusOutlineStyleOptions): GriffelStyle => {
const { outlineRadius, outlineColor, outlineOffset, outlineWidth } = options;

const outlineOffsetTop = (outlineOffset as FocusOutlineOffset)?.top || outlineOffset;
const outlineOffsetBottom = (outlineOffset as FocusOutlineOffset)?.bottom || outlineOffset;
const outlineOffsetLeft = (outlineOffset as FocusOutlineOffset)?.left || outlineOffset;
const outlineOffsetRight = (outlineOffset as FocusOutlineOffset)?.right || outlineOffset;
const { outlineRadius, outlineColor, outlineWidth } = options;

return {
...shorthands.borderColor('transparent'),
Expand All @@ -55,10 +70,10 @@ const getFocusOutlineStyles = (options: FocusOutlineStyleOptions): GriffelStyle
...shorthands.borderRadius(outlineRadius),
...shorthands.borderColor(outlineColor),

top: !outlineOffset ? `-${outlineWidth}` : `calc(0px - ${outlineWidth} - ${outlineOffsetTop})`,
bottom: !outlineOffset ? `-${outlineWidth}` : `calc(0px - ${outlineWidth} - ${outlineOffsetBottom})`,
left: !outlineOffset ? `-${outlineWidth}` : `calc(0px - ${outlineWidth} - ${outlineOffsetLeft})`,
right: !outlineOffset ? `-${outlineWidth}` : `calc(0px - ${outlineWidth} - ${outlineOffsetRight})`,
top: getOutlinePosition(options, 'top'),
right: getOutlinePosition(options, 'right'),
bottom: getOutlinePosition(options, 'bottom'),
left: getOutlinePosition(options, 'left'),
},
};
};
Expand Down