Skip to content

Commit

Permalink
Merge pull request #105 from gsoft-inc/feature/input-sizes
Browse files Browse the repository at this point in the history
added small size for text input / search input and passwordinput
  • Loading branch information
fraincs authored Mar 26, 2024
2 parents 0c5e578 + 2d37f82 commit 8c9ddb3
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/thirty-flowers-yell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@orbit-ui/transition-components": minor
---

Added support for size prop on TextInput / SearchInput and PasswordInput
17 changes: 16 additions & 1 deletion packages/components/src/input/src/Input.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
--o-ui-input-padding-with-icon: var(--hop-space-inset-xl);
--o-ui-input-padding-with-button: 2.5rem;
--o-ui-input-padding-loading: 2.5rem;
--o-ui-input-sm-padding: var(--hop-space-inset-squish-sm);
--o-ui-input-border-color: var(--hop-neutral-border);
--o-ui-input-border-radius: var(--hop-shape-rounded-md);
--o-ui-input-background-color: var(--hop-neutral-surface);
--o-ui-input-height: 2.5rem;
--o-ui-input-sm-height: 2rem;
}

.o-ui-input {
Expand All @@ -28,11 +31,15 @@
width: 100%;
background-color: inherit;
color: var(--hop-neutral-text);
min-height: calc(2.5rem - 2px);
min-height: calc(var(--o-ui-input-height) - 2px);
margin: 0;
padding: 0;
}

.o-ui-input-sm input {
min-height: calc(var(--o-ui-input-sm-height) - 2px);
}

.o-ui-input textarea {
width: 100%;
}
Expand All @@ -59,12 +66,20 @@
font-family: inherit;
}

.o-ui-text-input.o-ui-input-sm {
font-size: var(--hop-body-sm-font-size);
}

.o-ui-text-input input,
.o-ui-text-area textarea,
.o-ui-number-input input {
padding: var(--o-ui-input-padding);
}

.o-ui-text-input.o-ui-input-sm input {
padding: var(--o-ui-input-sm-padding);
}

.o-ui-text-input input,
.o-ui-text-area {
border-radius: var(--o-ui-input-border-radius);
Expand Down
7 changes: 5 additions & 2 deletions packages/components/src/input/src/useInput.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChangeEvent, ChangeEventHandler, ForwardedRef } from "react";
import { cssModule, isNil, isNumber, mergeClasses, useAutoFocus, useEventCallback, useMergedRefs } from "../../shared";
import { cssModule, isNil, isNumber, mergeClasses, normalizeSize, Size, useAutoFocus, useEventCallback, useMergedRefs } from "../../shared";

import { ValidationState } from "./types";

Expand All @@ -18,6 +18,7 @@ export interface UseInputProps {
placeholder?: string;
readOnly?: boolean;
required?: boolean;
size?: Size;
type?: "text" | "password" | "search" | "url" | "tel" | "email" | "number";
validationState?: ValidationState;
value?: string | number;
Expand All @@ -38,6 +39,7 @@ export function useInput({
placeholder,
readOnly,
required,
size,
type,
validationState,
value
Expand Down Expand Up @@ -81,7 +83,8 @@ export function useInput({
loading && "loading",
active && "active",
focus && "focus",
hover && "hover"
hover && "hover",
normalizeSize(size)
)
),
role: "presentation"
Expand Down
6 changes: 3 additions & 3 deletions packages/components/src/input/src/useInputContent.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { embedIconButton } from "../../button";
import { Spinner } from "../../spinner";
import { Spinner, SpinnerProps } from "../../spinner";
import { CreatedIconProps } from "@hopper-ui/icons";
import { ReactElement } from "react";
import { augmentElement } from "../../shared";
Expand All @@ -21,6 +21,6 @@ export function useInputButton(button: ReactElement, isActive: boolean, props: R
});
}

export function useInputSpinner(loading: boolean) {
return loading && <Spinner aria-label="Loading..." className="o-ui-input-spinner" role="presentation" />;
export function useInputSpinner(loading: boolean, props?: Partial<SpinnerProps>) {
return loading && <Spinner aria-label="Loading..." className="o-ui-input-spinner" role="presentation" {...props} />;
}
13 changes: 13 additions & 0 deletions packages/components/src/text-input/docs/TextInput.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@ A text input can show that it is currently loading data.
</Story>
</Preview>

### Size

A text input can vary in size.

<Preview>
<Story name="size">
<Inline alignY="center">
<TextInput placeholder="Where to?" size="sm" />
<TextInput placeholder="Where to?" />
</Inline>
</Story>
</Preview>

### Fluid

A text input can take the width of its container.
Expand Down
16 changes: 14 additions & 2 deletions packages/components/src/text-input/src/PasswordInput.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { AbstractTextInputProps, TextInput } from "./TextInput";
import { useState, ChangeEvent, ComponentProps, forwardRef } from "react";
import { EyeVisibleIcon, EyeHiddenIcon } from "@hopper-ui/icons";
import { OmitInternalProps, mergeProps, useControllableState, useEventCallback } from "../../shared";
import { OmitInternalProps, mergeProps, createSizeAdapter, useControllableState, useEventCallback } from "../../shared";
import { useResponsiveValue } from "../../styling";

import { IconButton } from "../../button";
import { useInputGroupTextInputProps } from "../../input-group";
Expand All @@ -10,13 +11,21 @@ const DefaultElement = "input";

export type InnerPasswordInputProps = AbstractTextInputProps<typeof DefaultElement>;

/* eslint-disable sort-keys, sort-keys-fix/sort-keys-fix */
const buttonSize = createSizeAdapter({
"sm": "xs",
"md": "sm"
});
/* eslint-enable sort-keys, sort-keys-fix/sort-keys-fix */

export function InnerPasswordInput(props: InnerPasswordInputProps) {
const [inputGroupProps] = useInputGroupTextInputProps();

const {
as = DefaultElement,
defaultValue,
forwardedRef,
size,
value,
wrapperProps,
...rest
Expand All @@ -28,6 +37,8 @@ export function InnerPasswordInput(props: InnerPasswordInputProps) {
const [inputValue, setValue] = useControllableState(value, defaultValue, "");
const [isHidden, setIsHidden] = useState(true);

const sizeValue = useResponsiveValue(size);

const handleChange = useEventCallback((event: ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
});
Expand All @@ -42,7 +53,7 @@ export function InnerPasswordInput(props: InnerPasswordInputProps) {
aria-label="Toggle password visibility"
className="o-ui-password-input-show-button"
onClick={handleShowValue}
size="sm"
size={buttonSize(sizeValue)}
title="Toggle password visibility"
variant="tertiary"
>
Expand All @@ -59,6 +70,7 @@ export function InnerPasswordInput(props: InnerPasswordInputProps) {
button: showButtonMarkup,
onChange: handleChange,
ref: forwardedRef,
size: size,
type: isHidden ? "password" as const : "text" as const,
value: inputValue,
wrapperProps: mergeProps(wrapperProps ?? {}, {
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/text-input/src/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function InnerSearchInput(props: InnerSearchInputProps) {
onChange,
onKeyDown,
onValueChange,
size,
value,
wrapperProps,
...rest
Expand Down Expand Up @@ -111,6 +112,7 @@ export function InnerSearchInput(props: InnerSearchInputProps) {
onChange: handleChange,
onKeyDown: handleKeyDown,
ref: inputRef,
size: size,
spellCheck: "false",
type: "search" as const,
value: inputValue,
Expand Down
34 changes: 28 additions & 6 deletions packages/components/src/text-input/src/TextInput.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { AbstractInputProps, adaptInputStylingProps, useInput, useInputButton, useInputHasFocus, useInputIcon, useInputSpinner } from "../../input";
import { AbstractInputProps, adaptInputStylingProps, useInput, useInputIcon, useInputSpinner, useInputButton, useInputHasFocus } from "../../input";
import { Box, BoxProps } from "../../box";
import { ChangeEvent, ComponentProps, ElementType, ReactElement, forwardRef } from "react";
import { ClearInputGroupContext, useInputGroupTextInputProps } from "../../input-group";
import { OmitInternalProps, cssModule, isNil, mergeProps, omitProps, useChainedEventCallback, useControllableState } from "../../shared";
import { OmitInternalProps, cssModule, createSizeAdapter, isNil, mergeProps, omitProps, useChainedEventCallback, useControllableState } from "../../shared";
import { ResponsiveProp, useResponsiveValue } from "../../styling";
import { useFieldInputProps } from "../../field";
import { useToolbarProps } from "../../toolbar";
Expand Down Expand Up @@ -35,6 +35,10 @@ export type AbstractTextInputProps<T extends ElementType> = AbstractInputProps<T
* @returns {void}
*/
onValueChange?: (event: ChangeEvent<HTMLInputElement>, value: string) => void;
/**
* An input can vary in size.
*/
size?: ResponsiveProp<"sm" | "md">;
/**
* A controlled value.
*/
Expand All @@ -54,6 +58,20 @@ export interface InnerTextInputProps extends AbstractTextInputProps<typeof Defau
type?: "text" | "password" | "search" | "url" | "tel" | "email";
}

/* eslint-disable sort-keys, sort-keys-fix/sort-keys-fix */
const spinnerSize = createSizeAdapter({
"sm": "md",
"md": "md"
});
/* eslint-enable sort-keys, sort-keys-fix/sort-keys-fix */

/* eslint-disable sort-keys, sort-keys-fix/sort-keys-fix */
const iconSize = createSizeAdapter({
"sm": "sm",
"md": "md"
});
/* eslint-enable sort-keys, sort-keys-fix/sort-keys-fix */

export function InnerTextInput(props: InnerTextInputProps) {
const [toolbarProps] = useToolbarProps();
const [fieldProps] = useFieldInputProps();
Expand Down Expand Up @@ -88,6 +106,7 @@ export function InnerTextInput(props: InnerTextInputProps) {
placeholder,
readOnly,
required,
size,
style,
type = "text",
validationState,
Expand All @@ -101,6 +120,7 @@ export function InnerTextInput(props: InnerTextInputProps) {
}

const fluidValue = useResponsiveValue(fluid);
const sizeValue = useResponsiveValue(size);

const [inputValue, setValue] = useControllableState(value, defaultValue, "");

Expand Down Expand Up @@ -129,18 +149,21 @@ export function InnerTextInput(props: InnerTextInputProps) {
placeholder,
readOnly,
required,
size: sizeValue,
type,
validationState,
value: inputValue
});

const { hasFocus, inputProps: inputFocusProps } = useInputHasFocus();

const iconMarkup = useInputIcon(icon);
const iconMarkup = useInputIcon(icon, {
size: iconSize(sizeValue)
});

const buttonMarkup = useInputButton(button, !disabled && !readOnly);
const buttonMarkup = useInputButton(button, !disabled && !readOnly, { size: sizeValue });

const loadingMarkup = useInputSpinner(loading);
const loadingMarkup = useInputSpinner(loading, { size: spinnerSize(sizeValue) });

const content = (
<>
Expand Down Expand Up @@ -189,7 +212,6 @@ export function InnerTextInput(props: InnerTextInputProps) {
);
}

InnerTextInput.defaultElement = DefaultElement;

/**
* A text input allow a user to enter and edit a text.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export const Default: PasswordInputStory = {
storyName: "default",
render: () => (
<Stack>
<PasswordInput aria-label="Label" />
<Inline alignY="center">
<PasswordInput aria-label="Label" />
<PasswordInput aria-label="Label" size="sm" />
</Inline>
<PasswordInput disabled aria-label="Label"></PasswordInput>
<PasswordInput readOnly aria-label="Label"></PasswordInput>
<PasswordInput fluid aria-label="Label"></PasswordInput>
Expand All @@ -29,7 +32,10 @@ export const Placeholder: PasswordInputStory = {
storyName: "placeholder",
render: () => (
<Stack>
<PasswordInput placeholder="What's your secret?" />
<Inline alignY="center">
<PasswordInput placeholder="What's your secret?" />
<PasswordInput placeholder="What's your secret?" size="sm" />
</Inline>
<PasswordInput disabled placeholder="What's your secret?"></PasswordInput>
<PasswordInput readOnly placeholder="What's your secret?"></PasswordInput>
<PasswordInput fluid placeholder="What's your secret?"></PasswordInput>
Expand All @@ -44,7 +50,10 @@ export const Value: PasswordInputStory = {
storyName: "value",
render: () => (
<Stack>
<PasswordInput defaultValue="test123!" aria-label="Label" />
<Inline alignY="center">
<PasswordInput defaultValue="test123!" aria-label="Label" />
<PasswordInput defaultValue="test123!" aria-label="Label" size="sm" />
</Inline>
<PasswordInput disabled defaultValue="test123!" aria-label="Label" />
<PasswordInput readOnly defaultValue="test123!" aria-label="Label" />
<Inline>
Expand Down Expand Up @@ -87,10 +96,16 @@ export const Zoom: PasswordInputStory = {
render: () => (
<Stack>
<Div className="zoom-in">
<PasswordInput placeholder="What's your secret?" />
<Inline alignY="center">
<PasswordInput placeholder="What's your secret?" />
<PasswordInput placeholder="What's your secret?" size="sm" />
</Inline>
</Div>
<Div className="zoom-out">
<PasswordInput placeholder="What's your secret?" />
<Inline alignY="center">
<PasswordInput placeholder="What's your secret?" />
<PasswordInput placeholder="What's your secret?" size="sm" />
</Inline>
</Div>
</Stack>
)
Expand Down
Loading

0 comments on commit 8c9ddb3

Please sign in to comment.