128 lines
5.5 KiB
JavaScript
128 lines
5.5 KiB
JavaScript
'use client';
|
|
import { tokens, typographyStyles } from '@fluentui/react-theme';
|
|
import { makeResetStyles, makeStyles, mergeClasses } from '@griffel/react';
|
|
export const fieldClassNames = {
|
|
root: `fui-Field`,
|
|
label: `fui-Field__label`,
|
|
validationMessage: `fui-Field__validationMessage`,
|
|
validationMessageIcon: `fui-Field__validationMessageIcon`,
|
|
hint: `fui-Field__hint`
|
|
};
|
|
// 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({
|
|
base: {
|
|
maxWidth: 'max-content',
|
|
maxHeight: 'max-content'
|
|
},
|
|
vertical: {
|
|
paddingTop: tokens.spacingVerticalXXS,
|
|
paddingBottom: tokens.spacingVerticalXXS,
|
|
marginBottom: tokens.spacingVerticalXXS
|
|
},
|
|
verticalLarge: {
|
|
paddingTop: '1px',
|
|
paddingBottom: '1px',
|
|
marginBottom: tokens.spacingVerticalXS
|
|
},
|
|
horizontal: {
|
|
paddingTop: tokens.spacingVerticalSNudge,
|
|
paddingBottom: tokens.spacingVerticalSNudge,
|
|
marginRight: tokens.spacingHorizontalM,
|
|
gridRowStart: '1',
|
|
gridRowEnd: '-1'
|
|
},
|
|
horizontalSmall: {
|
|
paddingTop: tokens.spacingVerticalXS,
|
|
paddingBottom: tokens.spacingVerticalXS
|
|
},
|
|
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: '9px',
|
|
paddingBottom: '9px'
|
|
}
|
|
});
|
|
const useSecondaryTextBaseClassName = makeResetStyles({
|
|
marginTop: tokens.spacingVerticalXXS,
|
|
color: tokens.colorNeutralForeground3,
|
|
...typographyStyles.caption1
|
|
});
|
|
const useSecondaryTextStyles = makeStyles({
|
|
error: {
|
|
color: tokens.colorPaletteRedForeground1
|
|
},
|
|
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} + ${tokens.spacingHorizontalXS})`
|
|
}
|
|
});
|
|
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} - ${tokens.spacingHorizontalXS})`,
|
|
marginRight: tokens.spacingHorizontalXS,
|
|
// 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: tokens.colorPaletteRedForeground1
|
|
},
|
|
warning: {
|
|
color: tokens.colorPaletteDarkOrangeForeground1
|
|
},
|
|
success: {
|
|
color: tokens.colorPaletteGreenForeground1
|
|
}
|
|
});
|
|
/**
|
|
* Apply styling to the Field slots based on the state
|
|
*/ export const useFieldStyles_unstable = (state)=>{
|
|
'use no memo';
|
|
const { validationState, size } = state;
|
|
const horizontal = state.orientation === 'horizontal';
|
|
const rootStyles = useRootStyles();
|
|
state.root.className = mergeClasses(fieldClassNames.root, rootStyles.base, horizontal && rootStyles.horizontal, horizontal && !state.label && rootStyles.horizontalNoLabel, state.root.className);
|
|
const labelStyles = useLabelStyles();
|
|
if (state.label) {
|
|
state.label.className = mergeClasses(fieldClassNames.label, labelStyles.base, horizontal && labelStyles.horizontal, horizontal && size === 'small' && labelStyles.horizontalSmall, horizontal && size === 'large' && labelStyles.horizontalLarge, !horizontal && labelStyles.vertical, !horizontal && size === 'large' && labelStyles.verticalLarge, state.label.className);
|
|
}
|
|
const validationMessageIconBaseClassName = useValidationMessageIconBaseClassName();
|
|
const validationMessageIconStyles = useValidationMessageIconStyles();
|
|
if (state.validationMessageIcon) {
|
|
state.validationMessageIcon.className = mergeClasses(fieldClassNames.validationMessageIcon, validationMessageIconBaseClassName, validationState !== 'none' && validationMessageIconStyles[validationState], state.validationMessageIcon.className);
|
|
}
|
|
const secondaryTextBaseClassName = useSecondaryTextBaseClassName();
|
|
const secondaryTextStyles = useSecondaryTextStyles();
|
|
if (state.validationMessage) {
|
|
state.validationMessage.className = mergeClasses(fieldClassNames.validationMessage, secondaryTextBaseClassName, validationState === 'error' && secondaryTextStyles.error, !!state.validationMessageIcon && secondaryTextStyles.withIcon, state.validationMessage.className);
|
|
}
|
|
if (state.hint) {
|
|
state.hint.className = mergeClasses(fieldClassNames.hint, secondaryTextBaseClassName, state.hint.className);
|
|
}
|
|
return state;
|
|
};
|