diff --git a/package.json b/package.json index d20c8681..f8249a6e 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "prepare": "husky install" }, "dependencies": { + "clsx": "^2.1.1", "next": "15.1.3", "react": "19.0.0", "react-dom": "19.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d37d44ed..6ccd5822 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + clsx: + specifier: ^2.1.1 + version: 2.1.1 next: specifier: 15.1.3 version: 15.1.3(@babel/core@7.26.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -2068,6 +2071,10 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -6899,6 +6906,8 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + clsx@2.1.1: {} + co@4.6.0: {} collect-v8-coverage@1.0.2: {} diff --git a/src/components/Sample.stories.tsx b/src/components/Sample.stories.tsx index ed945814..6846402f 100644 --- a/src/components/Sample.stories.tsx +++ b/src/components/Sample.stories.tsx @@ -1,7 +1,7 @@ -import type { ComponentStoryObj } from "@storybook/react"; +import type { StoryObj } from "@storybook/react"; import { Sample } from "./Sample"; -type Story = ComponentStoryObj; +type Story = StoryObj; const config = { component: Sample, diff --git a/src/components/comlete/button.module.css b/src/components/comlete/button.module.css new file mode 100644 index 00000000..9d97316a --- /dev/null +++ b/src/components/comlete/button.module.css @@ -0,0 +1,22 @@ +.button { + padding: 10px 20px; + border: none; + border-radius: 5px; + font-size: 16px; + cursor: pointer; + text-decoration: none; + width: 100%; + max-width: 320px; + display: flex; + align-items: center; + justify-content: center; + color: white; +} + +.primary { + background-color: #007bff; +} + +.secondary { + background-color: #6c757d; +} diff --git a/src/components/comlete/buttonCommon.tsx b/src/components/comlete/buttonCommon.tsx new file mode 100644 index 00000000..efe6024a --- /dev/null +++ b/src/components/comlete/buttonCommon.tsx @@ -0,0 +1,9 @@ +import clsx from "clsx"; +import styles from "./button.module.css"; + +export type ButtonColor = "primary" | "secondary"; + +const getButtonClassName = (color: ButtonColor) => + clsx(styles.button, styles[color]); + +export default getButtonClassName; diff --git a/src/components/comlete/buttton.stories.tsx b/src/components/comlete/buttton.stories.tsx new file mode 100644 index 00000000..d7224b3b --- /dev/null +++ b/src/components/comlete/buttton.stories.tsx @@ -0,0 +1,11 @@ +import type { StoryObj } from "@storybook/react"; +import { Button } from "./use"; + +type Story = StoryObj; + +const config = { + component: Button, +}; +export default config; + +export const Default: Story = {}; diff --git a/src/components/comlete/use.tsx b/src/components/comlete/use.tsx new file mode 100644 index 00000000..425d1343 --- /dev/null +++ b/src/components/comlete/use.tsx @@ -0,0 +1,8 @@ +import type { FC } from "react"; +import getButtonClassName from "./buttonCommon"; + +export const Button: FC = () => ( + +); diff --git a/src/components/initial/button.module.css b/src/components/initial/button.module.css new file mode 100644 index 00000000..9d97316a --- /dev/null +++ b/src/components/initial/button.module.css @@ -0,0 +1,22 @@ +.button { + padding: 10px 20px; + border: none; + border-radius: 5px; + font-size: 16px; + cursor: pointer; + text-decoration: none; + width: 100%; + max-width: 320px; + display: flex; + align-items: center; + justify-content: center; + color: white; +} + +.primary { + background-color: #007bff; +} + +.secondary { + background-color: #6c757d; +} diff --git a/src/components/initial/button.tsx b/src/components/initial/button.tsx new file mode 100644 index 00000000..5036a5b4 --- /dev/null +++ b/src/components/initial/button.tsx @@ -0,0 +1,22 @@ +import { + type ComponentPropsWithoutRef, + type ReactNode, + forwardRef, +} from "react"; +import getButtonClassName, { type ButtonColor } from "./buttonCommon"; + +type Props = { + children: ReactNode; + color?: ButtonColor; +} & ComponentPropsWithoutRef<"button">; + +export const Button = forwardRef(function button( + { children, type = "button", color = "primary", ...props }, + ref, +) { + return ( + + ); +}); diff --git a/src/components/initial/buttonCommon.tsx b/src/components/initial/buttonCommon.tsx new file mode 100644 index 00000000..efe6024a --- /dev/null +++ b/src/components/initial/buttonCommon.tsx @@ -0,0 +1,9 @@ +import clsx from "clsx"; +import styles from "./button.module.css"; + +export type ButtonColor = "primary" | "secondary"; + +const getButtonClassName = (color: ButtonColor) => + clsx(styles.button, styles[color]); + +export default getButtonClassName; diff --git a/src/components/initial/buttton.stories.tsx b/src/components/initial/buttton.stories.tsx new file mode 100644 index 00000000..fb078a5f --- /dev/null +++ b/src/components/initial/buttton.stories.tsx @@ -0,0 +1,16 @@ +import type { StoryObj } from "@storybook/react"; +import { Button } from "./button"; + +type Story = StoryObj; + +const config = { + component: Button, + args: { children: "ボタン" }, +}; +export default config; + +export const Default: Story = {}; + +export const Secondary: Story = { + args: { color: "secondary" }, +}; diff --git a/src/components/initial/link-button.tsx b/src/components/initial/link-button.tsx new file mode 100644 index 00000000..4991ff09 --- /dev/null +++ b/src/components/initial/link-button.tsx @@ -0,0 +1,25 @@ +import clsx from "clsx"; +import { + type ComponentPropsWithoutRef, + type ReactNode, + forwardRef, +} from "react"; +import styles from "./button.module.css"; +import getButtonClassName, { type ButtonColor } from "./buttonCommon"; + +type Props = { + children: ReactNode; + color?: ButtonColor; + href: string; +} & ComponentPropsWithoutRef<"a">; + +export const LinkButton = forwardRef(function button( + { children, href, color = "primary", ...props }, + ref, +) { + return ( + + {children} + + ); +}); diff --git a/src/components/initial/link-buttton.stories.tsx b/src/components/initial/link-buttton.stories.tsx new file mode 100644 index 00000000..d3fe3a9e --- /dev/null +++ b/src/components/initial/link-buttton.stories.tsx @@ -0,0 +1,16 @@ +import type { StoryObj } from "@storybook/react"; +import { LinkButton } from "./link-button"; + +type Story = StoryObj; + +const config = { + component: LinkButton, + args: { children: "ボタン", href: "/" }, +}; +export default config; + +export const Default: Story = {}; + +export const Secondary: Story = { + args: { color: "secondary" }, +}; diff --git a/src/components/modify/button.module.css b/src/components/modify/button.module.css new file mode 100644 index 00000000..9d97316a --- /dev/null +++ b/src/components/modify/button.module.css @@ -0,0 +1,22 @@ +.button { + padding: 10px 20px; + border: none; + border-radius: 5px; + font-size: 16px; + cursor: pointer; + text-decoration: none; + width: 100%; + max-width: 320px; + display: flex; + align-items: center; + justify-content: center; + color: white; +} + +.primary { + background-color: #007bff; +} + +.secondary { + background-color: #6c757d; +} diff --git a/src/components/modify/button.tsx b/src/components/modify/button.tsx new file mode 100644 index 00000000..9cfb95d3 --- /dev/null +++ b/src/components/modify/button.tsx @@ -0,0 +1,43 @@ +import { + type ComponentPropsWithoutRef, + type ElementType, + type ForwardedRef, + forwardRef, +} from "react"; +import getButtonClassName, { type ButtonColor } from "./buttonCommon"; + +type ThemeProps = { + color?: ButtonColor; +}; + +type DistributiveOmit = T extends unknown + ? Omit + : never; + +type As = { tag?: T }; + +type PolymorphicProps = As & + P & + DistributiveOmit, keyof P | "tag">; + +type Props = PolymorphicProps; + +export const Button = forwardRef(function button< + E extends ElementType = "button", +>( + { tag, color = "primary", children, disabled, ...props }: Props, + ref: ForwardedRef["ref"]>, +) { + const Tag = tag || "button"; + + return ( + + {children} + + ); +}); diff --git a/src/components/modify/buttonCommon.tsx b/src/components/modify/buttonCommon.tsx new file mode 100644 index 00000000..efe6024a --- /dev/null +++ b/src/components/modify/buttonCommon.tsx @@ -0,0 +1,9 @@ +import clsx from "clsx"; +import styles from "./button.module.css"; + +export type ButtonColor = "primary" | "secondary"; + +const getButtonClassName = (color: ButtonColor) => + clsx(styles.button, styles[color]); + +export default getButtonClassName; diff --git a/src/components/modify/buttton.stories.tsx b/src/components/modify/buttton.stories.tsx new file mode 100644 index 00000000..c850077b --- /dev/null +++ b/src/components/modify/buttton.stories.tsx @@ -0,0 +1,24 @@ +import type { StoryObj } from "@storybook/react"; +import { Button } from "./button"; + +type Story = StoryObj; + +const config = { + component: Button, + args: { children: "ボタン" }, +}; +export default config; + +export const Default: Story = {}; + +export const Secondary: Story = { + args: { color: "secondary" }, +}; + +export const Link: Story = { + args: { tag: "a", href: "/" }, +}; + +export const Div: Story = { + args: { tag: "div" }, +};