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
Expand Up @@ -23,6 +23,7 @@ import { DividerState } from '@fluentui/react-divider';
import type { DrawerBodyState } from '@fluentui/react-drawer';
import { DrawerFooterState } from '@fluentui/react-drawer';
import { DrawerHeaderState } from '@fluentui/react-drawer';
import { FieldState } from '@fluentui/react-field';
import { FlatTreeState } from '@fluentui/react-tree';
import { FluentProviderCustomStyleHooks } from '@fluentui/react-provider';
import { ImageState } from '@fluentui/react-image';
Expand Down Expand Up @@ -120,6 +121,9 @@ export const useSemanticDrawerFooterStyles: (_state: unknown) => DrawerFooterSta
// @public
export const useSemanticDrawerHeaderStyles: (_state: unknown) => DrawerHeaderState;

// @public
export const useSemanticFieldStyles: (_state: unknown) => FieldState;

// @public (undocumented)
export const useSemanticFlatTreeStyles: (_state: unknown) => FlatTreeState;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@fluentui/react-dialog": "^9.12.8",
"@fluentui/react-divider": "^9.2.86",
"@fluentui/react-drawer": "^9.7.8",
"@fluentui/react-field": "^9.2.6",
"@fluentui/react-icons": "^2.0.245",
"@fluentui/react-image": "^9.1.84",
"@fluentui/react-jsx-runtime": "^9.0.54",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useSemanticFieldStyles } from './useSemanticFieldStyles.styles';
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { makeResetStyles, makeStyles, mergeClasses } from '@griffel/react';
import { type FieldState, fieldClassNames } from '@fluentui/react-field';
import { getSlotClassNameProp_unstable } from '@fluentui/react-utilities';
import * as semanticTokens from '@fluentui/semantic-tokens';

// Size of the icon in the validation message
const iconSize = '12px';

/**
* Styles for the root slot
*/
const useRootStyles = makeStyles({
base: {
display: 'grid',
},

// In horizontal layout, the field is a grid with the label taking up the entire first column.
// The last row is slack space in case the label is taller than the rest of the content.
horizontal: {
gridTemplateColumns: '33% 1fr',
gridTemplateRows: 'auto auto auto 1fr',
},

// In horizontal layout without a label, replace the label's column with padding.
// This lets grid auto-flow properly place the other children, and keeps fields with and without labels aligned.
horizontalNoLabel: {
paddingLeft: '33%',
gridTemplateColumns: '1fr',
},
});

const useLabelStyles = makeStyles({
vertical: {
paddingTop: semanticTokens._ctrlFieldPaddingCtrlTextTop,
paddingBottom: semanticTokens._ctrlFieldPaddingCtrlTextBottom,
marginBottom: semanticTokens.gapBetweenContentXSmall,
},

verticalLarge: {
paddingTop: semanticTokens._ctrlFieldPaddingCtrlLgTextTop,
paddingBottom: semanticTokens._ctrlFieldPaddingCtrlLgTextBottom,
marginBottom: semanticTokens._ctrlFieldGapBetweenContentXSmall,
},

horizontal: {
paddingTop: semanticTokens._ctrlFieldPaddingCtrlTextTopHorizontal,
paddingBottom: semanticTokens._ctrlFieldPaddingCtrlTextBottomHorizontal,
marginRight: semanticTokens._ctrlFieldPaddingCtrlTextSide,
gridRowStart: '1',
gridRowEnd: '-1',
},

horizontalSmall: {
paddingTop: semanticTokens._ctrlFieldPaddingCtrlSmTextTop,
paddingBottom: semanticTokens._ctrlFieldPaddingCtrlSmTextBottom,
},

horizontalLarge: {
// To align the label text with the Input text, it should be centered within the 40px height of the Input.
// This is (40px - lineHeightBase400) / 2 = 9px. Hardcoded since there is no 9px padding token.
paddingTop: semanticTokens._ctrlFieldPaddingCtrlLgTextTopHorizontal,
paddingBottom: semanticTokens._ctrlFieldPaddingCtrlLgTextBottomHorizontal,
},
});

const useSecondaryTextBaseClassName = makeResetStyles({
marginTop: semanticTokens.gapBetweenContentXSmall,
color: semanticTokens._ctrlFieldForegroundCtrlNeutralSecondaryRest,
fontFamily: semanticTokens.textStyleDefaultRegularFontFamily,
fontWeight: semanticTokens.textStyleDefaultRegularWeight,
fontSize: semanticTokens.textRampMetadataFontSize,
lineHeight: semanticTokens.textRampMetadataLineHeight,
});

const useSecondaryTextStyles = makeStyles({
error: {
color: semanticTokens._ctrlFieldStatusDangerTintForeground,
},

withIcon: {
// Add a gutter for the icon, to allow multiple lines of text to line up to the right of the icon.
paddingLeft: `calc(${iconSize} + ${semanticTokens.gapBetweenTextSmall})`,
},
});

const useValidationMessageIconBaseClassName = makeResetStyles({
display: 'inline-block',
fontSize: iconSize,
// Negative left margin puts the icon in the gutter of the validation message div's withIcon style.
marginLeft: `calc(-${iconSize} - ${semanticTokens.gapBetweenTextSmall})`,
marginRight: semanticTokens.gapBetweenTextSmall,
// Line height of 0 prevents the verticalAlign from affecting the line height of the text.
lineHeight: '0',
// Negative verticalAlign shifts the inline icon down to align with the text (effectively top padding).
verticalAlign: '-1px',
});

const useValidationMessageIconStyles = makeStyles({
error: {
color: semanticTokens._ctrlFieldStatusDangerTintForeground,
},
warning: {
color: semanticTokens._ctrlFieldStatusWarningTintForeground,
},
success: {
color: semanticTokens._ctrlFieldStatusSuccessTintForeground,
},
});

/**
* Apply styling to the Field slots based on the state
*/
export const useSemanticFieldStyles = (_state: unknown): FieldState => {
'use no memo';

const state = _state as FieldState;
const { validationState, size } = state;
const horizontal = state.orientation === 'horizontal';

const rootStyles = useRootStyles();
state.root.className = mergeClasses(
state.root.className,
fieldClassNames.root,
rootStyles.base,
horizontal && rootStyles.horizontal,
horizontal && !state.label && rootStyles.horizontalNoLabel,
getSlotClassNameProp_unstable(state.root),
);

const labelStyles = useLabelStyles();
if (state.label) {
state.label.className = mergeClasses(
state.label.className,
fieldClassNames.label,
horizontal && labelStyles.horizontal,
horizontal && size === 'small' && labelStyles.horizontalSmall,
horizontal && size === 'large' && labelStyles.horizontalLarge,
!horizontal && labelStyles.vertical,
!horizontal && size === 'large' && labelStyles.verticalLarge,
getSlotClassNameProp_unstable(state.label),
);
}

const validationMessageIconBaseClassName = useValidationMessageIconBaseClassName();
const validationMessageIconStyles = useValidationMessageIconStyles();
if (state.validationMessageIcon) {
state.validationMessageIcon.className = mergeClasses(
state.validationMessageIcon.className,
fieldClassNames.validationMessageIcon,
validationMessageIconBaseClassName,
validationState !== 'none' && validationMessageIconStyles[validationState],
getSlotClassNameProp_unstable(state.validationMessageIcon),
);
}

const secondaryTextBaseClassName = useSecondaryTextBaseClassName();
const secondaryTextStyles = useSecondaryTextStyles();
if (state.validationMessage) {
state.validationMessage.className = mergeClasses(
state.validationMessage.className,
fieldClassNames.validationMessage,
secondaryTextBaseClassName,
validationState === 'error' && secondaryTextStyles.error,
!!state.validationMessageIcon && secondaryTextStyles.withIcon,
getSlotClassNameProp_unstable(state.validationMessage),
);
}

if (state.hint) {
state.hint.className = mergeClasses(
state.hint.className,
fieldClassNames.hint,
secondaryTextBaseClassName,
getSlotClassNameProp_unstable(state.hint),
);
}

return state;
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
useSemanticDrawerHeaderStyles,
useSemanticOverlayDrawerSurfaceStyles,
} from './Drawer';
import { useSemanticFieldStyles } from './Field';
import { useSemanticLabelStyles } from './Label';
import { useSemanticLinkStyles } from './Link';
import { useSemanticProgressBarStyles } from './ProgressBar/useSemanticProgressBarStyles.styles';
Expand Down Expand Up @@ -91,6 +92,8 @@ export const SEMANTIC_STYLE_HOOKS: FluentProviderCustomStyleHooks = {
useDrawerFooterStyles_unstable: useSemanticDrawerFooterStyles,
useDrawerHeaderStyles_unstable: useSemanticDrawerHeaderStyles,
useOverlayDrawerSurfaceStyles_unstable: useSemanticOverlayDrawerSurfaceStyles,
//Field styles
useFieldStyles_unstable: useSemanticFieldStyles,
// Label styles
useLabelStyles_unstable: useSemanticLabelStyles,
// Link styles
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export {
useSemanticDrawerHeaderStyles,
useSemanticOverlayDrawerSurfaceStyles,
} from './component-styles/Drawer';
export { useSemanticFieldStyles } from './component-styles/Field';
export { useSemanticLabelStyles } from './component-styles/Label';
export { useSemanticLinkStyles } from './component-styles/Link';
export { useSemanticProgressBarStyles } from './component-styles/ProgressBar/useSemanticProgressBarStyles.styles';
Expand Down
54 changes: 51 additions & 3 deletions packages/semantic-tokens/etc/semantic-tokens.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1711,6 +1711,54 @@ export const ctrlFabShadowRest = "var(--smtc-ctrl-fab-shadow-rest)";
// @public (undocumented)
export const ctrlFabShadowRestRaw = "--smtc-ctrl-fab-shadow-rest";

// @public
export const _ctrlFieldForegroundCtrlNeutralSecondaryRest = "var(--smtc-foreground-ctrl-neutral-secondary-rest, var(--colorNeutralForeground3))";

// @public
export const _ctrlFieldGapBetweenContentXSmall = "var(--smtc-gap-between-content-x-small, var(--spacingVerticalXS))";

// @public
export const _ctrlFieldPaddingCtrlLgTextBottom = "var(--smtc-padding-ctrl-lg-text-bottom, var(--smtc-padding-ctrl-lg-text-top, 1px))";

// @public
export const _ctrlFieldPaddingCtrlLgTextBottomHorizontal = "var(--smtc-padding-ctrl-lg-text-bottom, var(--smtc-padding-ctrl-lg-text-top, 9px))";

// @public
export const _ctrlFieldPaddingCtrlLgTextTop = "var(--smtc-padding-ctrl-lg-text-top, 1px)";

// @public
export const _ctrlFieldPaddingCtrlLgTextTopHorizontal = "var(--smtc-padding-ctrl-lg-text-top, 9px)";

// @public
export const _ctrlFieldPaddingCtrlSmTextBottom = "var(--smtc-padding-ctrl-sm-text-bottom, var(--smtc-padding-ctrl-sm-text-top, var(--spacingVerticalXS)))";

// @public
export const _ctrlFieldPaddingCtrlSmTextTop = "var(--smtc-padding-ctrl-sm-text-top, var(--spacingVerticalXS))";

// @public
export const _ctrlFieldPaddingCtrlTextBottom = "var(--smtc-padding-ctrl-text-bottom, var(--smtc-padding-ctrl-text-top, var(--spacingVerticalXXS)))";

// @public
export const _ctrlFieldPaddingCtrlTextBottomHorizontal = "var(--smtc-padding-ctrl-text-bottom, var(--smtc-padding-ctrl-text-top, var(--spacingVerticalSNudge)))";

// @public
export const _ctrlFieldPaddingCtrlTextSide = "var(--smtc-padding-ctrl-text-side, var(--spacingHorizontalM))";

// @public
export const _ctrlFieldPaddingCtrlTextTop = "var(--smtc-padding-ctrl-text-top, var(--spacingVerticalXXS))";

// @public
export const _ctrlFieldPaddingCtrlTextTopHorizontal = "var(--smtc-padding-ctrl-text-top, var(--spacingVerticalSNudge))";

// @public
export const _ctrlFieldStatusDangerTintForeground = "var(--smtc-status-danger-tint-foreground, var(--colorPaletteRedForeground1))";

// @public
export const _ctrlFieldStatusSuccessTintForeground = "var(--smtc-status-success-tint-foreground, var(--colorPaletteGreenForeground1))";

// @public
export const _ctrlFieldStatusWarningTintForeground = "var(--smtc-status-warning-tint-foreground, var(--colorPaletteDarkOrangeForeground1))";

// @public (undocumented)
export const ctrlFocusInnerStroke = "var(--smtc-ctrl-focus-inner-stroke, var(--colorStrokeFocus2))";

Expand Down Expand Up @@ -3545,7 +3593,7 @@ export const gapBetweenContentXLarge = "var(--smtc-gap-between-content-x-large)"
export const gapBetweenContentXLargeRaw = "--smtc-gap-between-content-x-large";

// @public (undocumented)
export const gapBetweenContentXSmall = "var(--smtc-gap-between-content-x-small)";
export const gapBetweenContentXSmall = "var(--smtc-gap-between-content-x-small, var(--spacingVerticalXXS))";

// @public (undocumented)
export const gapBetweenContentXSmallRaw = "--smtc-gap-between-content-x-small";
Expand Down Expand Up @@ -3611,7 +3659,7 @@ export const gapBetweenTextLarge = "var(--smtc-gap-between-text-large, var(--smt
export const gapBetweenTextLargeRaw = "--smtc-gap-between-text-large";

// @public (undocumented)
export const gapBetweenTextSmall = "var(--smtc-gap-between-text-small, var(--smtc-gap-between-content-xx-small))";
export const gapBetweenTextSmall = "var(--smtc-gap-between-text-small, var(--smtc-gap-between-content-xx-small, var(--spacingHorizontalXS)))";

// @public (undocumented)
export const gapBetweenTextSmallRaw = "--smtc-gap-between-text-small";
Expand Down Expand Up @@ -5695,7 +5743,7 @@ export const textRampMetadataFontSize = "var(--smtc-text-ramp-metadata-font-size
export const textRampMetadataFontSizeRaw = "--smtc-text-ramp-metadata-font-size";

// @public (undocumented)
export const textRampMetadataLineHeight = "var(--smtc-text-ramp-metadata-line-height, var(--smtc-text-global-caption1-line-height))";
export const textRampMetadataLineHeight = "var(--smtc-text-ramp-metadata-line-height, var(--smtc-text-global-caption1-line-height, var(--lineHeightBase200)))";

// @public (undocumented)
export const textRampMetadataLineHeightRaw = "--smtc-text-ramp-metadata-line-height";
Expand Down
3 changes: 2 additions & 1 deletion packages/semantic-tokens/src/control/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
spacingHorizontalXXL,
spacingHorizontalS,
spacingHorizontalL,
spacingVerticalXXS,
spacingHorizontalXS,
spacingHorizontalSNudge,
borderRadiusCircular,
Expand Down Expand Up @@ -343,7 +344,7 @@ export const paddingCtrlLgTextTop = `var(${paddingCtrlLgTextTopRaw})`;
export const paddingCtrlLgToNestedControl = `var(${paddingCtrlLgToNestedControlRaw})`;
export const gapBetweenContentNone = `var(${gapBetweenContentNoneRaw})`;
export const gapBetweenContentXxSmall = `var(${gapBetweenContentXxSmallRaw})`;
export const gapBetweenContentXSmall = `var(${gapBetweenContentXSmallRaw})`;
export const gapBetweenContentXSmall = `var(${gapBetweenContentXSmallRaw}, ${spacingVerticalXXS})`;
export const gapBetweenContentSmall = `var(${gapBetweenContentSmallRaw}, 8px)`;
export const gapBetweenCtrlDefault = `var(${gapBetweenCtrlDefaultRaw}, 8px)`;
export const gapBetweenContentMedium = `var(${gapBetweenContentMediumRaw})`;
Expand Down
Loading
Loading