Skip to content

Commit

Permalink
Merge pull request #24 from Atnic/feature/input
Browse files Browse the repository at this point in the history
Feature/input
  • Loading branch information
muhamien authored Feb 28, 2024
2 parents abb103e + 7e8c301 commit 02340a2
Show file tree
Hide file tree
Showing 16 changed files with 449 additions and 128 deletions.
41 changes: 14 additions & 27 deletions packages/components/button/stories/button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,16 @@ export default {
control: {
type: "select",
},
options: ["primary", "danger", "success", "warning", "transparent", "basic", "white"],
options: [
"primary",
"secondary",
"danger",
"success",
"warning",
"transparent",
// "basic",
"white",
],
},
size: {
control: {
Expand Down Expand Up @@ -55,38 +64,16 @@ export default {
type: "boolean",
},
},
// disableAnimation: {
// control: {
// type: "boolean",
// },
// },
},
} as Meta<typeof Button>;

const defaultProps = {
children: "Button",
children: "Base Button",
size: "md",
spinnerPlacement: "start",
...button.defaultVariants,
};

// const StateTemplate = (args: ButtonProps) => {
// const [isOpen, setIsOpen] = React.useState(false);
//
// const handlePress = () => {
// // eslint-disable-next-line no-console
// console.log("Pressed");
// setIsOpen((prev) => !prev);
// };
//
// return (
// <div className="h-screen">
// <Button {...args} aria-label="Open" aria-pressed={isOpen} onPress={handlePress}>
// {isOpen ? "Close" : "Open"}
// </Button>
// </div>
// );
// };

export const Solid = {
parameters: {
design: {
Expand Down Expand Up @@ -132,7 +119,7 @@ export const rounded = {
args: {
...defaultProps,
radius: "full",
variant: "rounded",
variant: "outline",
},
};
export const IconOnly = {
Expand All @@ -146,7 +133,7 @@ export const IconOnly = {
...defaultProps,
isIconOnly: true,
variant: "outline",
children: <JalaBiomass className="h-max w-max text-lg" />,
children: <JalaBiomass />,
},
};
export const IconWithText = {
Expand Down
1 change: 1 addition & 0 deletions packages/components/input/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"devDependencies": {
"@jala-banyu/theme": "workspace: *",
"@jala-banyu/system": "workspace: *",
"@jala-banyu/button": "workspace: *",
"clean-package": "2.2.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
Expand Down
5 changes: 4 additions & 1 deletion packages/components/input/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import Input from "./input";
import InputGroup from "./input-group";
import Textarea from "./textarea";

// export types
export type {InputProps} from "./input";
export type {InputGroupProps} from "./input-group";
export type {TextAreaProps} from "./textarea";

// export hooks
export {useInput} from "./use-input";
export {useInputGroup} from "./use-input-group";

// export component
export {Input, Textarea};
export {Input, InputGroup, Textarea};
8 changes: 8 additions & 0 deletions packages/components/input/src/input-group-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {createContext} from "@jala-banyu/react-utils";

import {ContextType} from "./use-input-group";

export const [InputGroupProvider, useInputGroupContext] = createContext<ContextType>({
name: "InputGroupContext",
strict: false,
});
26 changes: 26 additions & 0 deletions packages/components/input/src/input-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {forwardRef} from "@jala-banyu/system";
import React from "react";

import {InputGroupProvider} from "./input-group-context";
import {UseInputGroupProps, useInputGroup} from "./use-input-group";

export interface InputGroupProps extends UseInputGroupProps {}

const InputGroup = forwardRef<"div", InputGroupProps>((props, ref) => {
const {Component, domRef, context, children, getInputGroupProps} = useInputGroup({
...props,
ref,
});

return (
<InputGroupProvider value={context}>
<Component ref={domRef} {...getInputGroupProps()}>
{children}
</Component>
</InputGroupProvider>
);
});

InputGroup.displayName = "Banyu.InputGroup";

export default InputGroup;
91 changes: 91 additions & 0 deletions packages/components/input/src/use-input-group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import type {InputProps} from "./index";
import type {ReactRef} from "@jala-banyu/react-utils";
import type {InputGroupVariantProps} from "@jala-banyu/theme";

import {inputGroup} from "@jala-banyu/theme";
import {HTMLBanyuProps, PropGetter, mapPropsVariants} from "@jala-banyu/system";
import {useDOMRef} from "@jala-banyu/react-utils";
import {useMemo, useCallback} from "react";
interface Props extends HTMLBanyuProps, InputGroupVariantProps {
/**
* Ref to the DOM node.
*/
ref?: ReactRef<HTMLDivElement | null>;
/**
* Whether the buttons are disabled.
* @default false
*/
isDisabled?: InputProps["isDisabled"];
}

export type ContextType = {
size?: InputProps["size"];
variant?: InputProps["variant"];
radius?: InputProps["radius"];
isDisabled?: InputProps["isDisabled"];
disableAnimation?: InputProps["disableAnimation"];
};

export type UseInputGroupProps = Props &
Partial<Pick<InputProps, "size" | "label" | "radius" | "variant" | "disableAnimation">>;

export function useInputGroup(originalProps: UseInputGroupProps) {
const [props, variantProps] = mapPropsVariants(originalProps, inputGroup.variantKeys);

const {
ref,
as,
children,
size = "md",
variant = "flat",
isDisabled = false,
disableAnimation = false,
className,
...otherProps
} = props;

const Component = as || "div";

const domRef = useDOMRef(ref);

const classNames = useMemo(
() =>
inputGroup({
...variantProps,
className,
}),
[...Object.values(variantProps), className],
);

const context = useMemo<ContextType>(
() => ({
size,
isDisabled,
disableAnimation,
fullWidth: !!originalProps?.fullWidth,
}),
[size, variant, isDisabled, disableAnimation, originalProps?.fullWidth],
);

const getInputGroupProps: PropGetter = useCallback(
() => ({
role: "group",
"data-slot": "input-group",
class: classNames,
...otherProps,
}),
[otherProps],
);

return {
Component,
children,
domRef,
// label,
context,
classNames,
getInputGroupProps,
};
}

export type UseButtonGroupReturn = ReturnType<typeof useInputGroup>;
14 changes: 13 additions & 1 deletion packages/components/input/src/use-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {useMemo, Ref, useCallback, useState} from "react";
import {chain, mergeProps} from "@react-aria/utils";
import {useTextField} from "@react-aria/textfield";

import {useInputGroupContext} from "./input-group-context";

export interface Props<T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement>
extends Omit<HTMLBanyuProps<"input">, keyof InputVariantProps> {
/**
Expand Down Expand Up @@ -91,6 +93,8 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
originalProps: UseInputProps<T>,
) {
const [props, variantProps] = mapPropsVariants(originalProps, input.variantKeys);
const groupContext = useInputGroupContext();
const isInGroup = !!groupContext;

const {
ref,
Expand Down Expand Up @@ -220,11 +224,19 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
() =>
input({
...variantProps,
isInGroup,
isInvalid,
labelPlacement,
isClearable,
}),
[...Object.values(variantProps), isInvalid, labelPlacement, isClearable, hasStartContent],
[
...Object.values(variantProps),
isInvalid,
isInGroup,
labelPlacement,
isClearable,
hasStartContent,
],
);

const getBaseProps: PropGetter = useCallback(
Expand Down
72 changes: 72 additions & 0 deletions packages/components/input/stories/input-group.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* eslint-disable jsx-a11y/interactive-supports-focus */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import React from "react";
// @ts-ignore
import {Meta} from "@storybook/react";
import {inputGroup} from "@jala-banyu/theme";
import {Button} from "@jala-banyu/button";
import {ArrowRightIcon} from "@jala-banyu/shared-icons";

import {Input, InputGroup} from "../src";

export default {
title: "Components/InputGroup",
component: InputGroup,
argTypes: {
size: {
control: {
type: "select",
},
options: ["sm", "md", "lg"],
},
isDisabled: {
control: {
type: "boolean",
},
},
},
decorators: [
(Story) => (
<div className="w-400">
<Story />
</div>
),
],
} as Meta<typeof InputGroup>;

const defaultProps = {
...inputGroup.defaultVariants,
};

const Template = (args) => (
<div className="w-[600px]">
<InputGroup>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label className="flex items-center bg-neutral-200 text-neutral-800 px-3 text-sm borber-r border-brand">
Label
</label>
<Input {...args} placeholder="Firstname" />
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label className="flex items-center bg-neutral-200 text-neutral-800 px-3 text-sm borber-r border-brand">
<ArrowRightIcon />
</label>
<Input {...args} placeholder="Lastname" />
<Button {...args} className="min-w-[72px] !border-transparent" radius="none">
Button
</Button>
</InputGroup>
</div>
);

export const Default = {
render: Template,
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/file/T0TUGURgVGElV6MtU2EPYU/%5BJDS%5D-Design-System---Banyu-1.0?type=design&node-id=654-97121&mode=dev",
},
},
args: {
...defaultProps,
},
};
8 changes: 6 additions & 2 deletions packages/components/input/stories/input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React from "react";
import {Meta} from "@storybook/react";
import {input} from "@jala-banyu/theme";
import {Button} from "@jala-banyu/button";

import {Input} from "../src";

Expand Down Expand Up @@ -54,7 +55,7 @@ const defaultProps = {
};

const Template = (args) => (
<div className="w-full max-w-[400px]">
<div className="w-full max-w-[600px]">
<Input {...args} />
</div>
);
Expand Down Expand Up @@ -133,7 +134,7 @@ const EndContentTemplate = (args) => (
);

const StartAndEndContentTemplate = (args) => (
<div className="w-full max-w-max flex flex-col items-end gap-4">
<div className="w-full max-w-max flex-col items-end gap-4">
<Input
{...args}
defaultValue="jala"
Expand Down Expand Up @@ -161,6 +162,9 @@ const StartAndEndContentTemplate = (args) => (
}
type="url"
/>
<Button radius="lg" size="md">
Button
</Button>
</div>
);

Expand Down
4 changes: 2 additions & 2 deletions packages/components/modal/stories/modal.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ const content = (
</div>
</ModalBody>
<ModalFooter>
<Button fullWidth color="basic" radius="lg" size="md" onPress={onClose}>
<Button fullWidth color="secondary" radius="lg" size="md" onPress={onClose}>
Cancel
</Button>
<Button fullWidth color="primary" radius="lg" size="md" onPress={onClose}>
Expand Down Expand Up @@ -228,7 +228,7 @@ const WithDividerTemplate = (args: ModalProps) => {
</Button>
</ModalBody>
<ModalFooter>
<Button fullWidth color="basic" radius="lg" size="md" onPress={onClose}>
<Button fullWidth color="secondary" radius="lg" size="md" onPress={onClose}>
Cancel
</Button>
<Button fullWidth color="primary" radius="lg" size="md" onPress={onClose}>
Expand Down
Loading

0 comments on commit 02340a2

Please sign in to comment.