Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .changeset/mosaic-button-input-complexity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
10 changes: 0 additions & 10 deletions packages/ui/eslint-suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,6 @@
"count": 4
}
},
"src/mosaic/components/button/button.tsx": {
"complexity": {
"count": 1
}
},
"src/mosaic/components/input/input.tsx": {
"complexity": {
"count": 1
}
},
"src/mosaic/user-button/user-button.model.tsx": {
"complexity": {
"count": 1
Expand Down
70 changes: 48 additions & 22 deletions packages/ui/src/mosaic/components/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,53 @@ export function withTruncatableLabel(children: React.ReactNode): React.ReactNode
return result;
}

interface ButtonStyleOptions {
color: NonNullable<ButtonProps['color']>;
variant: NonNullable<ButtonProps['variant']>;
size: NonNullable<ButtonProps['size']>;
shape: NonNullable<ButtonProps['shape']>;
fullWidth: boolean;
touchTarget: boolean;
disabled: boolean;
defaults: React.ContextType<typeof ButtonContext>;
xstyle: ButtonProps['xstyle'];
}

function buttonStyleProps({
color,
variant,
size,
shape,
fullWidth,
touchTarget,
disabled,
defaults,
xstyle,
}: ButtonStyleOptions) {
const isIconShape = shape === 'square' || shape === 'circle';
const hasTouchTarget = touchTarget && variant !== 'link';
return stylex.props(
reset.base,
buttonScope,
// one ring for every color and variant — it reads as focus, not as the button's color
focusOutline.visible,
styles.base,
sizes[size],
variants[`${variant}-${color}`],
shape === 'square' && styles.shapeSquare,
shape === 'circle' && styles.shapeCircle,
isIconShape && iconSizes[size],
hasTouchTarget && styles.touchTarget,
hasTouchTarget && isIconShape && styles.touchTargetIcon,
defaults.sizeStyles,
isIconShape && defaults.iconStyles,
fullWidth && styles.fullWidth,
disabled && styles.disabled,
defaults.styles,
xstyle,
);
}

export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function MosaicButton(
{
color: colorProp,
Expand All @@ -116,35 +163,14 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function
const size = sizeProp ?? defaults.size ?? 'md';
const shape = shapeProp ?? defaults.shape ?? 'default';
const disabled = defaults.disabled || disabledProp || false;
const isIconShape = shape === 'square' || shape === 'circle';
const hasTouchTarget = touchTarget && variant !== 'link';
return (
<HeadlessButton
ref={ref}
disabled={disabled}
focusableWhenDisabled={focusableWhenDisabled}
{...mergeStyleProps(
themeProps('button', { color, variant, size, shape, fullWidth, disabled }),
stylex.props(
reset.base,
buttonScope,
// one ring for every color and variant — it reads as focus, not as the button's color
focusOutline.visible,
styles.base,
sizes[size],
variants[`${variant}-${color}`],
shape === 'square' && styles.shapeSquare,
shape === 'circle' && styles.shapeCircle,
isIconShape && iconSizes[size],
hasTouchTarget && styles.touchTarget,
hasTouchTarget && isIconShape && styles.touchTargetIcon,
defaults.sizeStyles,
isIconShape && defaults.iconStyles,
fullWidth && styles.fullWidth,
disabled && styles.disabled,
defaults.styles,
xstyle,
),
buttonStyleProps({ color, variant, size, shape, fullWidth, touchTarget, disabled, defaults, xstyle }),
rest,
)}
>
Expand Down
27 changes: 17 additions & 10 deletions packages/ui/src/mosaic/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export interface InputProps extends Omit<MosaicComponentProps<'input'>, 'size'>
variant?: InputVariant;
}

const variantStyles = {
default: inputStyles.base,
ghost: styles.ghost,
} as const;

export const Input = React.forwardRef<HTMLInputElement, InputProps>(function MosaicInput(
{
size: sizeProp,
Expand All @@ -45,30 +50,32 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(function Mos
ariaLabelledBy,
ariaDescribedBy,
});
const control = fieldProps ?? {
id,
disabled: disabledProp,
required: requiredProp,
'aria-invalid': ariaInvalid,
'aria-labelledby': ariaLabelledBy,
'aria-describedby': ariaDescribedBy,
};
const size = inputGroup?.size ?? sizeProp ?? 'md';
const disabled = inputGroup?.disabled || fieldProps?.disabled || disabledProp || false;
const required = fieldProps?.required ?? requiredProp;
const ariaInvalidValue = inputGroup?.invalid ? true : (fieldProps?.['aria-invalid'] ?? ariaInvalid);
const disabled = inputGroup?.disabled || control.disabled || false;

return useRender({
defaultTagName: 'input',
render,
ref: [forwardedRef, inputGroup?.inputRef],
props: {
...control,
disabled,
required,
id: fieldProps?.id ?? id,
'aria-invalid': ariaInvalidValue,
'aria-labelledby': fieldProps?.['aria-labelledby'] ?? ariaLabelledBy,
'aria-describedby': fieldProps?.['aria-describedby'] ?? ariaDescribedBy,
'aria-invalid': inputGroup?.invalid ? true : control['aria-invalid'],
...mergeStyleProps(
themeProps('input', { size, variant, disabled }),
stylex.props(
reset.base,
styles.base,
sizes[size],
variant === 'default' && inputStyles.base,
variant === 'ghost' && styles.ghost,
variantStyles[variant],
variant === 'default' && disabled && inputStyles.disabled,
xstyle,
),
Expand Down
Loading