Skip to content

Commit

Permalink
Fix mobile input element typography settings (#2450)
Browse files Browse the repository at this point in the history
* Fix mobile input element typography settings

* Select field added
  • Loading branch information
isaozler authored Aug 1, 2024
1 parent bc52918 commit 85d84e7
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/three-turtles-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@kadena/kode-ui': minor
---

Fix mobile input element typography settings
31 changes: 26 additions & 5 deletions packages/libs/kode-ui/src/components/Form/Form.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const statusOutlineColor = createVar();

export const outlineColor = createVar();
export const iconFill = fallbackVar(token('color.icon.base.@init'));
const textColor = fallbackVar(token('color.text.base.@init'));
const textColor = fallbackVar(token('color.text.gray.default'));

export const outlineStyles = {
outlineStyle: `solid`,
Expand Down Expand Up @@ -192,10 +192,31 @@ export const inputSizeVariants = {

export const inputFontTypeVariants = {
fontType: {
ui: uiSmallRegular,
code: monospaceSmallRegular,
// The mobile selector is required for a11y purposes.
ui: {
uiSmallRegular,
'&[data-is-mobile]': {
// @TODO: this should reference the mobile token inputUiMobileRegular
fontFamily: `${token('typography.family.primaryFont')} !important`,
fontSize: `${token('typography.fontSize.base')} !important`,
fontWeight: `${token('typography.weight.primaryFont.regular')} !important`,
lineHeight: `${token('typography.lineHeight.xl')} !important`,
letterSpacing: `${token('spacing.n0')} !important`,
},
},
code: {
monospaceSmallRegular,
'&[data-is-mobile]': {
// @TODO: this should reference the mobile token inputMonospaceMobileRegular
fontFamily: `${token('typography.family.monospaceFont')} !important`,
fontSize: `${token('typography.fontSize.base')} !important`,
fontWeight: `${token('typography.weight.monospaceFont.regular')} !important`,
lineHeight: `${token('typography.lineHeight.xl')} !important`,
letterSpacing: `${token('spacing.n0')} !important`,
},
},
},
} as const;
} as { fontType: { ui: any; code: any } };

export const inputSizeCompoundVariants: {
variants: {
Expand Down Expand Up @@ -258,7 +279,7 @@ export const input = recipe({
border: 'none',
backgroundColor: token('color.background.input.default'),
borderRadius: '0',
color: textColor,
color: token('color.text.base.@init'),
transition: 'box-shadow, background-color 0.2s ease-in-out',
'::placeholder': {
color: textColor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import type {
ForwardedRef,
ReactElement,
} from 'react';
import React, { forwardRef, useCallback } from 'react';
import React, { forwardRef, useCallback, useState } from 'react';
import type { AriaNumberFieldProps } from 'react-aria';
import { useFocusRing, useHover, useLocale, useNumberField } from 'react-aria';
import { useNumberFieldState } from 'react-stately';
import { useMobile } from '../../../utils';
import { Field } from '../Field/Field';
import { input } from '../Form.css';
import type { IFormFieldHeaderProps } from '../FormFieldHeader/FormFieldHeader';
Expand Down Expand Up @@ -90,12 +91,15 @@ export function NumberFieldBase(
isTextInput: true,
autoFocus: props.autoFocus,
});
const [isTouched, setIsTouched] = useState(false);
const { isMobile } = useMobile();

// handle uncontrollable form state eg. react-hook-form
const handleOnChange = useCallback(
(event: ChangeEvent<ElementRef<'input'>>) => {
inputProps.onChange?.(event);
props.onChange?.(event);
setIsTouched(isTouched && !!event.target.value);
},
[props.onChange, inputProps.onChange],
);
Expand Down Expand Up @@ -129,7 +133,8 @@ export function NumberFieldBase(
ref={ref}
className={classNames(
input({
variant: fieldProps.isInvalid ? 'negative' : props.variant,
variant:
fieldProps.isInvalid && isTouched ? 'negative' : props.variant,
size,
fontType,
}),
Expand All @@ -143,6 +148,7 @@ export function NumberFieldBase(
data-positive={props.isPositive || undefined}
data-has-start-addon={!!props.startVisual || undefined}
data-has-end-addon
data-is-mobile={isMobile || undefined}
/>
</Field>
);
Expand Down
4 changes: 4 additions & 0 deletions packages/libs/kode-ui/src/components/Form/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Popover } from '../../Popover';

import type { RecipeVariants } from '@vanilla-extract/recipes';
import classNames from 'classnames';
import { useMobile } from '../../../utils';
import { Field } from '../Field/Field';
import { input } from '../Form.css';
import type { FormFieldDirection } from '../FormFieldHeader/FormFieldHeader';
Expand Down Expand Up @@ -69,6 +70,8 @@ function SelectBase<T extends object>(
ref,
);

const { isMobile } = useMobile();

return (
<Field
{...fieldProps}
Expand Down Expand Up @@ -113,6 +116,7 @@ function SelectBase<T extends object>(
isInvalid={fieldProps.isInvalid}
isPositive={props.isPositive}
elementType="button"
data-is-mobile={isMobile || undefined}
>
<span
{...valueProps}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import type {
ForwardedRef,
ReactElement,
} from 'react';
import React, { forwardRef, useCallback } from 'react';
import React, { forwardRef, useCallback, useState } from 'react';
import type { AriaTextFieldProps } from 'react-aria';
import { useFocusRing, useHover, useTextField } from 'react-aria';
import { useMobile } from '../../../utils';
import { Field } from '../Field/Field';
import { input } from '../Form.css';
import type { IFormFieldHeaderProps } from '../FormFieldHeader/FormFieldHeader';
Expand Down Expand Up @@ -63,6 +64,7 @@ export function TextFieldBase(
forwardedRef: ForwardedRef<ElementRef<'input'>>,
) {
const ref = useObjectRef<ElementRef<'input'>>(forwardedRef);
const [isTouched, setIsTouched] = useState(false);
const isDisabled = props.isDisabled || props.disabled;
const { inputProps, ...fieldProps } = useTextField(
{
Expand All @@ -79,12 +81,14 @@ export function TextFieldBase(
isTextInput: true,
autoFocus: props.autoFocus,
});
const { isMobile } = useMobile();

// handle uncontrollable form state eg. react-hook-form
const handleOnChange = useCallback(
(event: ChangeEvent<ElementRef<'input'>>) => {
inputProps.onChange?.(event);
props.onChange?.(event);
setIsTouched(isTouched && !!event.target.value);
},
[props.onChange, inputProps.onChange],
);
Expand All @@ -111,7 +115,7 @@ export function TextFieldBase(
ref={ref}
className={classNames(
input({
variant: fieldProps.isInvalid ? 'negative' : variant,
variant: fieldProps.isInvalid && isTouched ? 'negative' : variant,
size,
fontType,
}),
Expand All @@ -124,6 +128,7 @@ export function TextFieldBase(
data-invalid={fieldProps.isInvalid || undefined}
data-has-start-addon={!!startVisual || undefined}
data-has-end-addon={!!endAddon || undefined}
data-is-mobile={isMobile || undefined}
/>
</Field>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import type {
ForwardedRef,
ReactNode,
} from 'react';
import React, { forwardRef, useCallback } from 'react';
import React, { forwardRef, useCallback, useState } from 'react';
import type { AriaTextFieldProps } from 'react-aria';
import { useFocusRing, useHover, useTextField } from 'react-aria';
import { useMobile } from '../../../utils';
import { Field } from '../Field/Field';
import type { InputVariants } from '../Form.css';
import { input } from '../Form.css';
Expand Down Expand Up @@ -90,6 +91,9 @@ export function TextareaFieldBase(
() => {},
);

const [isTouched, setIsTouched] = useState(false);
const { isMobile } = useMobile();

const onHeightChange = useCallback(() => {
if (props.autoResize && ref.current) {
const input = ref.current;
Expand Down Expand Up @@ -122,6 +126,7 @@ export function TextareaFieldBase(
inputProps.onChange?.(event);
setInputValue(event.target.value);
props.onChange?.(event);
setIsTouched(isTouched && !!event.target.value);
},
[props.onChange, inputProps.onChange],
);
Expand Down Expand Up @@ -149,7 +154,7 @@ export function TextareaFieldBase(
ref={ref}
className={classNames(
input({
variant: fieldProps.isInvalid ? 'negative' : variant,
variant: fieldProps.isInvalid && isTouched ? 'negative' : variant,
size,
fontType,
}),
Expand All @@ -163,6 +168,7 @@ export function TextareaFieldBase(
data-focus-visible={isFocusVisible || undefined}
data-has-start-addon={!!startVisual || undefined}
data-has-end-addon={!!endAddon || undefined}
data-is-mobile={isMobile || undefined}
/>
</Field>
);
Expand Down
1 change: 1 addition & 0 deletions packages/libs/kode-ui/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export * from './is';
export * from './object';
export * from './testId';
export * from './useAsyncFn';
export * from './useMobile';
export * from './useMountedState';
export * from './useTheme';
28 changes: 28 additions & 0 deletions packages/libs/kode-ui/src/utils/useMobile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useEffect, useState } from 'react';

/**
* A hook that returns whether the component is rendered in a mobile browser.
* It is useful for applying specific styling or UX for mobile or desktop clients.
*/
export function useMobile(): { isMobile: boolean } {
const [isMobile, setIsMobile] = useState(false);

useEffect(() => {
[
'Android',
'webOS',
'iPhone',
'iPad',
'iPod',
'BlackBerry',
'Windows Phone',
'Opera Mini',
'IEMobile',
'Mobile',
].some((keyword) => setIsMobile(navigator.userAgent.indexOf(keyword) > -1));
}, []);

return {
isMobile,
};
}

0 comments on commit 85d84e7

Please sign in to comment.