diff --git a/packages/propel/package.json b/packages/propel/package.json index f0d991852b4..be0be893d89 100644 --- a/packages/propel/package.json +++ b/packages/propel/package.json @@ -100,6 +100,10 @@ "import": "./dist/icons/index.mjs", "require": "./dist/icons/index.js" }, + "./input": { + "import": "./dist/input/index.mjs", + "require": "./dist/input/index.js" + }, "./menu": { "import": "./dist/menu/index.mjs", "require": "./dist/menu/index.js" diff --git a/packages/propel/src/input/index.ts b/packages/propel/src/input/index.ts new file mode 100644 index 00000000000..4ce4a889370 --- /dev/null +++ b/packages/propel/src/input/index.ts @@ -0,0 +1 @@ +export * from "./input"; diff --git a/packages/propel/src/input/input.stories.tsx b/packages/propel/src/input/input.stories.tsx new file mode 100644 index 00000000000..76648eaf89a --- /dev/null +++ b/packages/propel/src/input/input.stories.tsx @@ -0,0 +1,153 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { Input } from "./index"; + +const meta: Meta = { + title: "Components/Input", + component: Input, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + mode: { + control: "select", + options: ["primary", "transparent", "true-transparent"], + }, + inputSize: { + control: "select", + options: ["xs", "sm", "md"], + }, + hasError: { + control: "boolean", + }, + type: { + control: "select", + options: ["text", "email", "password", "number", "tel", "url", "search"], + }, + autoComplete: { + control: "select", + options: ["on", "off"], + }, + disabled: { + control: "boolean", + }, + }, +}; + +export default meta; +type Story = StoryObj; + +const createStory = (args: Partial>): Story => ({ + args: { placeholder: "Enter text...", className: "w-[400px]", ...args }, +}); + +const createShowcaseStory = ( + title: string, + sections: Array<{ label: string; props: Partial> }> +): Story => ({ + render: () => ( +
+
+

{title}

+
+ {sections.map(({ label, props }, index) => ( +
+ + +
+ ))} +
+
+
+ ), +}); + +export const Default = createStory({}); + +export const Primary = createStory({ + mode: "primary", + placeholder: "Primary input", +}); + +export const Transparent = createStory({ + mode: "transparent", + placeholder: "Transparent input", +}); + +export const TrueTransparent = createStory({ + mode: "true-transparent", + placeholder: "True transparent input", +}); + +export const ExtraSmall = createStory({ + inputSize: "xs", + placeholder: "Extra small input", +}); + +export const Small = createStory({ + inputSize: "sm", + placeholder: "Small input", +}); + +export const Medium = createStory({ + inputSize: "md", + placeholder: "Medium input", +}); + +export const WithError = createStory({ + hasError: true, + placeholder: "Input with error", + defaultValue: "Invalid input", +}); + +export const Disabled = createStory({ + disabled: true, + placeholder: "Disabled input", + defaultValue: "Cannot edit this", +}); + +export const WithValue = createStory({ + defaultValue: "Pre-filled value", + placeholder: "Input with value", +}); + +export const Email = createStory({ + type: "email", + placeholder: "Enter your email", + autoComplete: "on", +}); + +export const Password = createStory({ + type: "password", + placeholder: "Enter your password", + autoComplete: "off", +}); + +export const Number = createStory({ + type: "number", + placeholder: "Enter a number", +}); + +export const Search = createStory({ + type: "search", + placeholder: "Search...", +}); + +export const AllModes = createShowcaseStory("Input Modes", [ + { label: "Primary", props: { mode: "primary", placeholder: "Primary input" } }, + { label: "Transparent", props: { mode: "transparent", placeholder: "Transparent input" } }, + { label: "True Transparent", props: { mode: "true-transparent", placeholder: "True transparent input" } }, +]); + +export const AllSizes = createShowcaseStory("Input Sizes", [ + { label: "Extra Small (xs)", props: { inputSize: "xs", placeholder: "Extra small input" } }, + { label: "Small (sm)", props: { inputSize: "sm", placeholder: "Small input" } }, + { label: "Medium (md)", props: { inputSize: "md", placeholder: "Medium input" } }, +]); + +export const AllStates = createShowcaseStory("Input States", [ + { label: "Normal", props: { placeholder: "Normal input" } }, + { label: "With Error", props: { hasError: true, placeholder: "Input with error" } }, + { label: "Disabled", props: { disabled: true, placeholder: "Disabled input" } }, + { label: "With Value", props: { defaultValue: "Pre-filled value", placeholder: "Input with value" } }, +]); diff --git a/packages/propel/src/input/input.tsx b/packages/propel/src/input/input.tsx new file mode 100644 index 00000000000..04034b9391e --- /dev/null +++ b/packages/propel/src/input/input.tsx @@ -0,0 +1,54 @@ +import * as React from "react"; +import { Input as BaseInput } from "@base-ui-components/react/input"; +// helpers +import { cn } from "../utils"; + +export interface InputProps extends React.InputHTMLAttributes { + mode?: "primary" | "transparent" | "true-transparent"; + inputSize?: "xs" | "sm" | "md"; + hasError?: boolean; +} + +const Input = React.forwardRef((props, ref) => { + const { + id, + type, + name, + mode = "primary", + inputSize = "sm", + hasError = false, + className = "", + autoComplete = "off", + ...rest + } = props; + + return ( + + ); +}); + +Input.displayName = "form-input-field"; + +export { Input }; diff --git a/packages/propel/tsdown.config.ts b/packages/propel/tsdown.config.ts index 4a2ff851120..162bea54621 100644 --- a/packages/propel/tsdown.config.ts +++ b/packages/propel/tsdown.config.ts @@ -18,6 +18,7 @@ export default defineConfig({ "src/emoji-reaction/index.ts", "src/emoji-reaction-picker/index.ts", "src/icons/index.ts", + "src/input/index.ts", "src/menu/index.ts", "src/pill/index.ts", "src/popover/index.ts", diff --git a/packages/ui/src/popovers/popover-menu.stories.tsx b/packages/ui/src/popovers/popover-menu.stories.tsx index b868c9d01e9..4e8a56cda6b 100644 --- a/packages/ui/src/popovers/popover-menu.stories.tsx +++ b/packages/ui/src/popovers/popover-menu.stories.tsx @@ -1,5 +1,5 @@ -import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; +import React from "react"; import { PopoverMenu } from "./popover-menu"; type TPopoverMenu = {