Skip to content

Commit

Permalink
Merge pull request #13 from tuzkituan/main
Browse files Browse the repository at this point in the history
Version 1.1.7
  • Loading branch information
tuzkituan authored Oct 27, 2023
2 parents 9f97535 + 4e5ea34 commit 6d13569
Show file tree
Hide file tree
Showing 30 changed files with 9,824 additions and 253 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# ZENI-UI
I just want to create my own UI library, that's all.

[![npm](https://img.shields.io/npm/v/zeni-ui)](https://www.npmjs.com/package/zeni-ui)
[![npm](https://img.shields.io/npm/dm/zeni-ui)](https://www.npmjs.com/package/zeni-ui)

### Storybook
https://tuzkituan.github.io/zeni-ui/

I just want to create my own UI library, that's all.
2 changes: 1 addition & 1 deletion lib/components/alert/Alert.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from "@storybook/react";
import { Alert } from "./Alert";

const meta = {
title: "ATOMS/Alert",
title: "FEEDBACK/Alert",
component: Alert,
tags: ["autodocs"],
argTypes: {
Expand Down
2 changes: 1 addition & 1 deletion lib/components/avatar/Avatar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from "@storybook/react";
import { Avatar } from "./Avatar";

const meta = {
title: "ATOMS/Avatar",
title: "MEDIA/Avatar",
component: Avatar,
tags: ["autodocs"],
parameters: {
Expand Down
2 changes: 1 addition & 1 deletion lib/components/button/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Button } from "./Button";

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
const meta = {
title: "ATOMS/Button",
title: "FORMS/Button",
component: Button,
parameters: {
layout: "centered",
Expand Down
8 changes: 7 additions & 1 deletion lib/components/checkbox/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from "@storybook/react";
import { Checkbox } from "./Checkbox";

const meta = {
title: "ATOMS/Checkbox",
title: "FORMS/Checkbox/Checkbox",
component: Checkbox,
tags: ["autodocs"],
argTypes: {
Expand All @@ -14,6 +14,12 @@ const meta = {
isChecked: {
control: { type: "boolean" },
},
isReadOnly: {
control: { type: "boolean" },
},
isDisabled: {
control: { type: "boolean" },
},
onChange: {
action: "onChange",
},
Expand Down
117 changes: 99 additions & 18 deletions lib/components/checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { twMerge } from "tailwind-merge";
import { useComponentStyle } from "../../customization/styles/theme.context";
import { ICheckbox } from "./Checkbox.types";
import { useMemo } from "react";
import { ICheckbox, ICheckboxGroup } from "./Checkbox.types";
import React, { ChangeEvent, useEffect, useMemo, useState } from "react";

const defaultProps: Partial<ICheckbox> = {
children: undefined,
Expand All @@ -22,8 +22,14 @@ export const Checkbox = (props: ICheckbox) => {
} = { ...defaultProps, ...props };

const containerClasses = useMemo(() => {
return twMerge(theme.container(), className);
}, [className, theme]);
return twMerge(
theme.container(),
theme.label({
disabled: isDisabled,
}),
className
);
}, [className, isDisabled, theme]);

const inputClasses = useMemo(() => {
return twMerge(
Expand All @@ -36,17 +42,8 @@ export const Checkbox = (props: ICheckbox) => {
);
}, [className, size, isReadOnly, isIndeterminate, theme]);

const labelClasses = useMemo(() => {
return twMerge(
theme.label({
disabled: isDisabled,
}),
className
);
}, [className, isDisabled, theme]);

return (
<div
<label
className={containerClasses}
style={{
...(spacing
Expand All @@ -57,7 +54,6 @@ export const Checkbox = (props: ICheckbox) => {
}}
>
<input
id="checkbox"
aria-describedby="checkbox"
type="checkbox"
className={inputClasses}
Expand All @@ -71,9 +67,94 @@ export const Checkbox = (props: ICheckbox) => {
}}
{...restProps}
/>
<label htmlFor="checkbox" className={labelClasses}>
{children}
</label>
{children}
</label>
);
};

export const CheckboxGroup = ({
children,
defaultValue,
value,
onChange,
layout = "horizontal",
spacing,
}: ICheckboxGroup) => {
const theme = useComponentStyle("Checkbox");
const [selectedValues, setSelectedValues] = useState(defaultValue || []);

useEffect(() => {
setSelectedValues(value || defaultValue || []);
}, [value, defaultValue]);

const handleCheckboxChange = (
optionValue?: string | number,
isChecked?: boolean
) => {
if (!optionValue) {
return;
}
if (isChecked) {
setSelectedValues((prevSelectedValues) => [
...prevSelectedValues,
optionValue,
]);
} else {
setSelectedValues((prevSelectedValues) =>
prevSelectedValues.filter((value) => value !== optionValue)
);
}
};

useEffect(() => {
if (onChange) {
onChange(selectedValues);
}
}, [selectedValues, onChange]);

const containerClasses = useMemo(() => {
return twMerge(
theme.group({
layout,
})
);
}, [layout, theme]);

if (!children) {
return null;
}
return (
<div
className={containerClasses}
style={{
...(spacing
? {
gap: spacing,
}
: null),
}}
>
{React.Children.map(children, (child) => {
if (React.isValidElement(child) && child.type === Checkbox) {
const { value: optionValue, ...checkboxProps } =
child.props as ICheckbox;
const isChecked = selectedValues.includes(optionValue || "");
return (
<Checkbox
{...checkboxProps}
isChecked={isChecked}
onChange={(event: ChangeEvent<HTMLInputElement>) =>
handleCheckboxChange(optionValue, event.target.checked)
}
/>
);
} else {
console.error(
"CheckboxGroup only accepts Checkbox components as children"
);
return null;
}
})}
</div>
);
};
10 changes: 10 additions & 0 deletions lib/components/checkbox/Checkbox.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,20 @@ export interface ICheckbox
"size" | "checked" | "readonly" | "disabled"
> {
children?: React.ReactNode;
value?: string | number;
spacing?: string | number;
size?: "sm" | "md" | "lg";
isDisabled?: boolean;
isChecked?: boolean;
isIndeterminate?: boolean;
isReadOnly?: boolean;
}

export interface ICheckboxGroup {
children?: React.ReactNode;
defaultValue?: (string | number)[];
value?: (string | number)[];
onChange?: (value?: (string | number)[]) => void;
layout?: "horizontal" | "vertical";
spacing?: string | number;
}
50 changes: 50 additions & 0 deletions lib/components/checkbox/CheckboxGroup.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { Meta, StoryObj } from "@storybook/react";

import { ICheckboxGroup } from "./Checkbox.types";
import { Checkbox, CheckboxGroup } from "./Checkbox";

const meta = {
title: "FORMS/Checkbox/CheckboxGroup",
component: CheckboxGroup,
tags: ["autodocs"],
argTypes: {
layout: {
options: ["horizontal", "vertical"],
control: { type: "radio" },
},
defaultValue: {
control: { type: "text" },
},
spacing: {
control: { type: "number" },
},
value: {
control: { type: "text" },
},
onChange: {
action: "onChange",
},
},
} satisfies Meta<typeof CheckboxGroup>;

export default meta;
type Story = StoryObj<ICheckboxGroup>;

const radioGroupArgs: ICheckboxGroup = {
value: ["1"],
layout: "horizontal",
onChange: (val) => console.log("checked: ", val),
};

export const Primary: Story = (props: ICheckboxGroup) => {
const all = { ...radioGroupArgs, ...props };
return (
<CheckboxGroup {...all}>
<Checkbox value="1">Option A</Checkbox>
<Checkbox value="2">Option B</Checkbox>
<Checkbox value="3">Option C</Checkbox>
</CheckboxGroup>
);
};

Primary.args = radioGroupArgs;
48 changes: 18 additions & 30 deletions lib/components/input/Input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@ import type { Meta, StoryObj } from "@storybook/react";

import { Check } from "@phosphor-icons/react";
import { Button } from "../button/Button";
import {
Input,
InputGroup,
InputLeftElement,
InputRightElement,
} from "./Input";
import { Input } from "./Input";

const meta = {
title: "INPUTS/Input",
title: "FORMS/Input",
component: Input,
tags: ["autodocs"],
argTypes: {
Expand All @@ -37,42 +32,35 @@ type Story = StoryObj<typeof meta>;

export const Primary: Story = {
args: {
className: "",
variant: "outline",
size: "md",
placeholder: "Input your name",
},
};

type InputGroupStory = StoryObj<typeof InputGroup>;

export const InputGroupWithLeftElement: InputGroupStory = {
render: ({ ...args }) => {
export const InputWithLeftElement: Story = {
render: () => {
return (
<InputGroup {...args}>
<InputLeftElement>
<Check />
</InputLeftElement>
<Input size="md" variant="filled" placeholder="Enter your email" />
</InputGroup>
<Input
leftElement={<Check />}
size="md"
variant="filled"
placeholder="Enter your email"
/>
);
},
args: {},
};

export const InputGroupWithRightElement: InputGroupStory = {
render: ({ ...args }) => {
export const InputWithRightElement: Story = {
render: () => {
return (
<InputGroup {...args}>
<Input
size="md"
variant="outline"
placeholder="Enter your value here"
/>
<InputRightElement>
<Button size="xs">Show</Button>
</InputRightElement>
</InputGroup>
<Input
rightElement={<Button size="xs">Show</Button>}
size="md"
variant="outline"
placeholder="Enter your email"
/>
);
},
args: {},
Expand Down
Loading

0 comments on commit 6d13569

Please sign in to comment.