Skip to content

Commit

Permalink
refactor(inputs): ♻️ update textarea, add invalid & loading (#235)
Browse files Browse the repository at this point in the history
  • Loading branch information
navin-moorthy authored Dec 23, 2021
1 parent 6501dba commit 384fd69
Show file tree
Hide file tree
Showing 27 changed files with 773 additions and 247 deletions.
17 changes: 11 additions & 6 deletions src/input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,33 @@ import * as React from "react";

import { RenderProp } from "../utils";

import { InputMain, InputMainHTMLProps } from "./InputMain";
import { InputBase, InputBaseHTMLProps } from "./InputBase";
import { InputPrefix } from "./InputPrefix";
import { useInputProps } from "./InputProps";
import { InputInitialState, InputStateReturn } from "./InputState";
import { InputSuffix } from "./InputSuffix";
import { InputWrapper } from "./InputWrapper";

export type InputOwnProps = InputMainHTMLProps & {};
export type InputOwnProps = InputBaseHTMLProps & {};

export type InputProps = InputInitialState &
InputOwnProps &
RenderProp<InputStateReturn>;

export const Input = React.forwardRef<HTMLInputElement, InputProps>(
(props, ref) => {
const { state, wrapperProps, mainProps, prefixProps, suffixProps } =
useInputProps(props);
const { prefix, suffix } = state;
const {
wrapperProps,
mainProps,
prefixProps,
suffixProps,
suffix,
prefix,
} = useInputProps(props);

return (
<InputWrapper {...wrapperProps}>
<InputMain ref={ref} {...mainProps} />
<InputBase ref={ref} {...mainProps} />
{prefix ? <InputPrefix {...prefixProps} /> : null}
{suffix ? <InputSuffix {...suffixProps} /> : null}
</InputWrapper>
Expand Down
55 changes: 55 additions & 0 deletions src/input/InputBase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { createComponent, createHook } from "reakit-system";
import { InputHTMLProps, InputOptions, useInput } from "reakit";
import { ariaAttr } from "@renderlesskit/react";

import { useTheme } from "../theme";
import { cx } from "../utils";

import { INPUT_BASE_KEYS } from "./__keys";
import { InputStateReturn } from "./InputState";

export type InputBaseOptions = InputOptions &
Pick<
InputStateReturn,
"size" | "variant" | "prefix" | "suffix" | "invalid"
> & {};

export type InputBaseHTMLProps = Omit<InputHTMLProps, "size" | "prefix">;

export type InputBaseProps = InputBaseOptions & InputBaseHTMLProps;

export const useInputBase = createHook<InputBaseOptions, InputBaseHTMLProps>({
name: "InputBase",
compose: useInput,
keys: INPUT_BASE_KEYS,

useProps(options, htmlProps) {
const { size, variant, prefix, suffix, invalid, disabled } = options;
const { className: htmlClassName, ...restHtmlProps } = htmlProps;

const theme = useTheme("input");
const className = cx(
theme.base.common,
theme.base.size[size].common,
!prefix || !suffix ? theme.base.size[size].withoutAddon : "",
theme.base.variant[variant].common,
disabled || invalid ? "" : theme.base.variant[variant].interactions,
disabled ? theme.base.variant[variant].disabled : "",
invalid ? theme.base.variant[variant].invalid : "",
htmlClassName,
);

return {
className,
disabled,
"aria-invalid": ariaAttr(invalid),
...restHtmlProps,
};
},
});

export const InputBase = createComponent({
as: "input",
memo: true,
useHook: useInputBase,
});
46 changes: 0 additions & 46 deletions src/input/InputMain.tsx

This file was deleted.

14 changes: 10 additions & 4 deletions src/input/InputPrefix.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import { useTheme } from "../theme";
import { cx } from "../utils";

import { INPUT_PREFIX_KEYS } from "./__keys";
import { InputProps } from "./Input";
import { InputStateReturn } from "./InputState";

export type InputPrefixOptions = BoxOptions &
Pick<InputStateReturn, "size" | "variant"> & {};
Pick<InputStateReturn, "size" | "variant" | "invalid"> & {
disabled: InputProps["disabled"];
};

export type InputPrefixHTMLProps = BoxHTMLProps;

Expand All @@ -23,14 +26,17 @@ export const useInputPrefix = createHook<
keys: INPUT_PREFIX_KEYS,

useProps(options, htmlProps) {
const { size, variant } = options;
const { size, variant, invalid, disabled } = options;
const { className: htmlClassName, ...restHtmlProps } = htmlProps;

const theme = useTheme("input");
const className = cx(
theme.prefix.base,
theme.prefix.common,
theme.prefix.size[size],
theme.prefix.variant[variant],
theme.prefix.variant[variant].common,
disabled || invalid ? "" : theme.prefix.variant[variant].interactions,
disabled ? theme.prefix.variant[variant].disabled : "",
invalid ? theme.prefix.variant[variant].invalid : "",
htmlClassName,
);

Expand Down
21 changes: 17 additions & 4 deletions src/input/InputProps.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react";

import { getComponentProps } from "../index";
import { Spinner } from "../spinner";
import { runIfFn, splitProps, withIconA11y } from "../utils";

import { USE_INPUT_STATE_KEYS } from "./__keys";
Expand Down Expand Up @@ -29,17 +30,24 @@ const componentMap = {

export const useInputProps = (props: React.PropsWithChildren<InputProps>) => {
const [state, inputProps] = useInputStateSplit(props);
const { prefix, suffix } = state;
const { className, style, children, ...restProps } = inputProps;
const { prefix, suffix, loading, size } = state;
const { className, style, children, disabled, ...restProps } = inputProps;
const { componentProps } = getComponentProps(componentMap, children, state);

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, setHasPaddingCalculated] = React.useState(false);

const _prefix: InputProps["prefix"] =
componentProps?.prefixProps?.children || prefix;
const _suffix: InputProps["suffix"] =
const __suffix: InputProps["suffix"] =
componentProps?.suffixProps?.children || suffix;
const _suffix: InputProps["suffix"] = React.useMemo(() => {
return loading ? (
<Spinner size={size !== "xl" ? "xs" : "md"} />
) : (
withIconA11y(runIfFn(__suffix, state))
);
}, [__suffix, loading, size, state]);

const inputInlineStyles = React.useRef<Record<string, any>>({});
const prefixRef = React.useRef<HTMLElement>(null);
Expand Down Expand Up @@ -78,27 +86,32 @@ export const useInputProps = (props: React.PropsWithChildren<InputProps>) => {

const mainProps: InputWrapperProps = {
...state,
disabled,
...restProps,
style: { ...inputInlineStyles.current },
...componentProps.mainProps,
};

const prefixProps: InputPrefixProps = {
...state,
disabled,
...componentProps.prefixProps,
ref: prefixRef,
children: withIconA11y(runIfFn(_prefix, state)),
};

const suffixProps: InputSuffixProps = {
...state,
disabled,
...componentProps.suffixProps,
ref: suffixRef,
children: withIconA11y(runIfFn(_suffix, state)),
children: _suffix,
};

return {
state,
prefix: _prefix,
suffix: _suffix,
wrapperProps,
mainProps,
prefixProps,
Expand Down
32 changes: 26 additions & 6 deletions src/input/InputState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ export type InputState = {
*
* @default md
*/
size: keyof Renderlesskit.GetThemeValue<"input", "main", "size">;
size: keyof Renderlesskit.GetThemeValue<"input", "base", "size">;

/**
* How the input should look?
*
* @default solid
*/
variant: keyof Renderlesskit.GetThemeValue<"input", "main", "variant">;
variant: keyof Renderlesskit.GetThemeValue<"input", "base", "variant">;

/**
* Description for the Switch.
Expand All @@ -24,18 +24,38 @@ export type InputState = {
* Description for the Switch.
*/
suffix: RenderPropType<InputStateReturn>;

/**
* True, if the value of the input is invalid.
*/
invalid?: boolean;

/**
* True, if the input is loading.
*/
loading?: boolean;
};

export type InputActions = {};

export type InputStateReturn = InputState & InputActions;

export type InputInitialState = Partial<
Pick<InputState, "prefix" | "suffix" | "size" | "variant">
Pick<
InputState,
"prefix" | "suffix" | "size" | "variant" | "invalid" | "loading"
>
> & {};

export function useInputState(props: InputInitialState = {}): InputStateReturn {
const { prefix, suffix, size = "md", variant = "outline" } = props;

return { prefix, suffix, size, variant };
const {
prefix,
suffix,
size = "md",
variant = "outline",
invalid = false,
loading = false,
} = props;

return { prefix, suffix, size, variant, invalid, loading };
}
16 changes: 11 additions & 5 deletions src/input/InputSuffix.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import { useTheme } from "../theme";
import { cx } from "../utils";

import { INPUT_SUFFIX_KEYS } from "./__keys";
import { InputProps } from "./Input";
import { InputStateReturn } from "./InputState";

export type InputSuffixOptions = BoxOptions &
Pick<InputStateReturn, "size" | "variant"> & {};
Pick<InputStateReturn, "size" | "variant" | "invalid"> & {
disabled: InputProps["disabled"];
};

export type InputSuffixHTMLProps = BoxHTMLProps;

Expand All @@ -23,14 +26,17 @@ export const useInputSuffix = createHook<
keys: INPUT_SUFFIX_KEYS,

useProps(options, htmlProps) {
const { size, variant } = options;
const { size, variant, disabled, invalid } = options;
const { className: htmlClassName, ...restHtmlProps } = htmlProps;

const theme = useTheme("input");
const theme = useTheme("select");
const className = cx(
theme.suffix.base,
theme.suffix.common,
theme.suffix.size[size],
theme.suffix.variant[variant],
theme.suffix.variant[variant].common,
disabled || invalid ? "" : theme.suffix.variant[variant].interactions,
disabled ? theme.suffix.variant[variant].disabled : "",
invalid ? theme.suffix.variant[variant].invalid : "",
htmlClassName,
);

Expand Down
8 changes: 5 additions & 3 deletions src/input/__keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ export const USE_INPUT_STATE_KEYS = [
"suffix",
"size",
"variant",
"invalid",
"loading",
] as const;
export const INPUT_STATE_KEYS = USE_INPUT_STATE_KEYS;
export const INPUT_MAIN_KEYS = INPUT_STATE_KEYS;
export const INPUT_PREFIX_KEYS = INPUT_MAIN_KEYS;
export const INPUT_BASE_KEYS = INPUT_STATE_KEYS;
export const INPUT_PREFIX_KEYS = [...INPUT_BASE_KEYS, "disabled"] as const;
export const INPUT_SUFFIX_KEYS = INPUT_PREFIX_KEYS;
export const INPUT_WRAPPER_KEYS = INPUT_SUFFIX_KEYS;
export const INPUT_WRAPPER_KEYS = INPUT_BASE_KEYS;
8 changes: 8 additions & 0 deletions src/input/stories/InputBasic.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ export const Disabled: Story = {
args: { placeholder: "Search", disabled: true },
};

export const Invalid: Story = {
args: { placeholder: "Search", invalid: true },
};

export const Prefix: Story = {
args: {
placeholder: "Search",
Expand All @@ -106,3 +110,7 @@ export const PrefixSuffix: Story = {
suffix: <CaretDownIcon />,
},
};

export const Loading: Story = {
args: { placeholder: "Search", loading: true },
};
Loading

1 comment on commit 384fd69

@vercel
Copy link

@vercel vercel bot commented on 384fd69 Dec 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.