diff --git a/src/components/MUI/Inputs/Autocomplete.stories.tsx b/src/components/MUI/Inputs/Autocomplete.stories.tsx new file mode 100644 index 0000000..96ec452 --- /dev/null +++ b/src/components/MUI/Inputs/Autocomplete.stories.tsx @@ -0,0 +1,153 @@ +import * as React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; +import { Autocomplete } from "./Autocomplete"; +import { TextField } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; +import { AutocompleteRenderInputParams } from "@mui/material/Autocomplete"; + +const elements = [ + "Hydrogen", + "Helium", + "Lithium", + "Beryllium", + "Boron", + "Carbon", + "Nitrogen", + "Oxygen", + "Fluorine", + "Neon", + "Sodium", + "Magnesium", +]; + +type CommonArgs = ExtraArgs & { + options: string[]; + freeSolo: boolean; + disableClearable: boolean; + autoHighlight: boolean; + autoSelect: boolean; + filterSelectedOptions: boolean; + size: "small" | "medium"; + limitTags?: number; +}; + +type SingleArgs = CommonArgs & { + multiple?: false; + defaultValue?: string | null; +}; + +type MultipleArgs = CommonArgs & { + multiple: true; + defaultValue?: string[]; +}; + +type StoryArgs = SingleArgs | MultipleArgs; + +type ExtraArgs = { + label: string; + placeholder?: string; + tfVariant: "outlined" | "filled" | "standard"; + tfColor: "primary" | "secondary" | "success" | "error" | "info" | "warning"; +}; + +const meta: Meta = { + title: "MUI/Inputs/Autocomplete", + component: Autocomplete, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + options: { control: "object" }, + freeSolo: { control: "boolean" }, + disableClearable: { control: "boolean" }, + autoHighlight: { control: "boolean" }, + autoSelect: { control: "boolean" }, + filterSelectedOptions: { control: "boolean" }, + size: { control: "select", options: ["small", "medium"] }, + tfVariant: { + name: "TextField variant", + control: "select", + options: ["outlined", "filled", "standard"], + }, + tfColor: { + name: "TextField color", + control: "select", + options: ["primary", "secondary", "success", "error", "info", "warning"], + }, + label: { control: "text" }, + placeholder: { control: "text" }, + defaultValue: { control: false }, + }, + args: { + options: elements, + freeSolo: false, + disableClearable: false, + autoHighlight: true, + autoSelect: false, + filterSelectedOptions: true, + size: "medium", + tfVariant: "outlined", + tfColor: "primary", + label: "Select element", + placeholder: "Type to search…", + defaultValue: null, + }, +}; + +export default meta; +type Story = StoryObj; + +const withTextField = (args: StoryArgs) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { defaultValue, ...rest } = args; + + return { + ...rest, + renderInput: (params: AutocompleteRenderInputParams) => ( + + ), + }; +}; + +export const Basic: Story = { + render: (args: StoryArgs) => , +}; + +export const FreeSoloMode: Story = { + parameters: { + docs: { + description: { + story: + "Enables freeSolo mode, allowing users to enter values that are not present in the options list. This behaves like a text input with suggestions rather than a strict selector.", + }, + }, + }, + args: { + freeSolo: true, + disableClearable: true, + }, + render: (args: StoryArgs) => , +}; + +export const MultipleSelections: StoryObj = { + args: { + multiple: true, + defaultValue: [], + }, + render: (args) => { + const [value, setValue] = React.useState(args.defaultValue ?? []); + return ( + setValue(newValue ?? [])} + /> + ); + }, +}; diff --git a/src/components/MUI/Inputs/Autocomplete.tsx b/src/components/MUI/Inputs/Autocomplete.tsx new file mode 100644 index 0000000..f8c81e0 --- /dev/null +++ b/src/components/MUI/Inputs/Autocomplete.tsx @@ -0,0 +1,9 @@ +import MuiAutocomplete from "@mui/material/Autocomplete"; + +export const Autocomplete = MuiAutocomplete as typeof MuiAutocomplete & { + displayName?: string; +}; + +Autocomplete.displayName = "Autocomplete"; + +export type { AutocompleteProps } from "@mui/material/Autocomplete"; diff --git a/src/components/MUI/Inputs/Button.stories.tsx b/src/components/MUI/Inputs/Button.stories.tsx new file mode 100644 index 0000000..69df02f --- /dev/null +++ b/src/components/MUI/Inputs/Button.stories.tsx @@ -0,0 +1,171 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { + AddIcon, + Box, + Button, + DeleteIcon, + SaveIcon, + SendIcon, + Stack, +} from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +const meta: Meta = { + title: "MUI/Inputs/Button", + component: Button, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + variant: { + control: "select", + options: ["text", "contained", "outlined"], + }, + color: { + control: "select", + options: [ + "inherit", + "primary", + "secondary", + "success", + "error", + "info", + "warning", + ], + }, + size: { + control: "select", + options: ["small", "medium", "large"], + }, + href: { control: "text" }, + rel: { control: "text" }, + children: { name: "label", control: "text" }, + }, + args: { + children: "Button", + variant: "contained", + color: "primary", + size: "medium", + disabled: false, + disableElevation: false, + fullWidth: false, + href: "", + rel: "", + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => ( + + ), +}; + +export const Variants: Story = { + render: (_args) => ( + + + + + + ), +}; + +export const Sizes: Story = { + render: (_args) => ( + + + + + + ), +}; + +export const Colours: Story = { + render: (_args) => ( + + + + + + + + + ), +}; + +export const WithStartIcon: Story = { + render: (_args) => ( + + + + + ), +}; + +export const WithEndIcon: Story = { + render: (_args) => ( + + + + + ), +}; + +export const States: Story = { + render: (_args) => ( + + + + + + + + + + + + + ), +}; diff --git a/src/components/MUI/Inputs/ButtonGroup.stories.tsx b/src/components/MUI/Inputs/ButtonGroup.stories.tsx new file mode 100644 index 0000000..401de23 --- /dev/null +++ b/src/components/MUI/Inputs/ButtonGroup.stories.tsx @@ -0,0 +1,61 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Button, ButtonGroup } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +const meta: Meta = { + title: "MUI/Inputs/ButtonGroup", + component: ButtonGroup, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + variant: { control: "select", options: ["text", "outlined", "contained"] }, + color: { + control: "select", + options: [ + "inherit", + "primary", + "secondary", + "success", + "error", + "info", + "warning", + ], + }, + size: { control: "select", options: ["small", "medium", "large"] }, + orientation: { control: "select", options: ["horizontal", "vertical"] }, + disabled: { control: "boolean" }, + fullWidth: { control: "boolean" }, + }, + args: { + variant: "contained", + color: "primary", + size: "medium", + orientation: "horizontal", + disabled: false, + fullWidth: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => ( + + + + + + ), +}; + +export const Vertical: Story = { + args: { orientation: "vertical" }, + render: (args) => ( + + + + + + ), +}; diff --git a/src/components/MUI/Inputs/Checkbox.stories.tsx b/src/components/MUI/Inputs/Checkbox.stories.tsx new file mode 100644 index 0000000..58e7d1f --- /dev/null +++ b/src/components/MUI/Inputs/Checkbox.stories.tsx @@ -0,0 +1,73 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import * as React from "react"; +import { Box, Checkbox } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +const meta: Meta = { + title: "MUI/Inputs/Checkbox", + component: Checkbox, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + color: { + control: "select", + options: [ + "default", + "primary", + "secondary", + "success", + "error", + "info", + "warning", + ], + }, + size: { control: "select", options: ["small", "medium", "large"] }, + disabled: { control: "boolean" }, + indeterminate: { control: "boolean" }, + }, + args: { + color: "primary", + size: "medium", + disabled: false, + indeterminate: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = {}; + +export const Controlled: Story = { + render: (args) => { + const [checked, setChecked] = React.useState(true); + return ( + setChecked(e.target.checked)} + /> + ); + }, +}; + +export const Colours: Story = { + render: (args) => ( + + + + + + + ), +}; + +export const Sizes: Story = { + render: (args) => ( + + + + + + ), +}; diff --git a/src/components/MUI/Inputs/Fab.stories.tsx b/src/components/MUI/Inputs/Fab.stories.tsx new file mode 100644 index 0000000..d4e9b6e --- /dev/null +++ b/src/components/MUI/Inputs/Fab.stories.tsx @@ -0,0 +1,93 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { + AddIcon, + Box, + DeleteIcon, + Fab, + FolderIcon, + SaveIcon, + WorkIcon, +} from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +const meta: Meta = { + title: "MUI/Inputs/Floating Action Button", + component: Fab, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + color: { + control: "select", + options: [ + "default", + "inherit", + "primary", + "secondary", + "success", + "error", + "info", + "warning", + ], + }, + size: { control: "select", options: ["small", "medium", "large"] }, + variant: { control: "select", options: ["circular", "extended"] }, + disabled: { control: "boolean" }, + }, + args: { + color: "primary", + size: "medium", + variant: "circular", + disabled: false, + }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => ( + + + + ), +}; + +export const Colors: Story = { + render: (args) => ( + + + + + + + + + + + + + + + + + + + + + ), +}; + +export const Extended: Story = { + args: { variant: "extended" }, + render: (args) => ( + + + + Add + + + + Delete + + + ), +}; diff --git a/src/components/MUI/Inputs/Radio.stories.tsx b/src/components/MUI/Inputs/Radio.stories.tsx new file mode 100644 index 0000000..e7db931 --- /dev/null +++ b/src/components/MUI/Inputs/Radio.stories.tsx @@ -0,0 +1,67 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import * as React from "react"; +import { FormControlLabel, Radio, RadioGroup } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +type RArgs = React.ComponentProps; + +const meta: Meta = { + title: "MUI/Inputs/Radio", + component: Radio, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + color: { + control: "select", + options: [ + "default", + "primary", + "secondary", + "success", + "error", + "info", + "warning", + ], + }, + size: { control: "select", options: ["small", "medium", "large"] }, + disabled: { control: "boolean" }, + }, + args: { + color: "primary", + size: "medium", + disabled: false, + }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { render: (args) => }; + +export const GroupControlled: Story = { + render: (args) => { + const [value, setValue] = React.useState("a"); + return ( + setValue((e.target as HTMLInputElement).value)} + row + > + } + label="Option A" + /> + } + label="Option B" + /> + } + label="Option C" + /> + + ); + }, +}; diff --git a/src/components/MUI/Inputs/RadioGroup.stories.tsx b/src/components/MUI/Inputs/RadioGroup.stories.tsx new file mode 100644 index 0000000..56ac68c --- /dev/null +++ b/src/components/MUI/Inputs/RadioGroup.stories.tsx @@ -0,0 +1,37 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { FormControlLabel, Radio, RadioGroup } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +const meta: Meta = { + title: "MUI/Inputs/RadioGroup", + component: RadioGroup, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + row: { control: "boolean" }, + }, + args: { row: true }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => ( + + } label="Option A" /> + } label="Option B" /> + } label="Option C" /> + + ), +}; + +export const Vertical: Story = { + args: { row: false }, + render: (args) => ( + + } label="Alpha" /> + } label="Beta" /> + } label="Gamma" /> + + ), +}; diff --git a/src/components/MUI/Inputs/Rating.stories.tsx b/src/components/MUI/Inputs/Rating.stories.tsx new file mode 100644 index 0000000..bc03c5e --- /dev/null +++ b/src/components/MUI/Inputs/Rating.stories.tsx @@ -0,0 +1,42 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import * as React from "react"; +import { Rating } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +const meta: Meta = { + title: "MUI/Inputs/Rating", + component: Rating, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + value: { control: { type: "number", min: 0, max: 5, step: 0.5 } }, + precision: { + control: "select", + options: [1.0, 0.5, 0.25, 0.2, 0.1], + }, + max: { control: { type: "number", min: 1, max: 10, step: 1 } }, + readOnly: { control: "boolean" }, + disabled: { control: "boolean" }, + size: { control: "select", options: ["small", "medium", "large"] }, + }, + args: { + value: 3.5, + precision: 0.5, + max: 5, + readOnly: false, + disabled: false, + size: "medium", + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => , +}; + +export const ReadOnly: Story = { + args: { readOnly: true, value: 4 }, + render: (args) => , +}; diff --git a/src/components/MUI/Inputs/Select.stories.tsx b/src/components/MUI/Inputs/Select.stories.tsx new file mode 100644 index 0000000..0db1725 --- /dev/null +++ b/src/components/MUI/Inputs/Select.stories.tsx @@ -0,0 +1,86 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Box, FormControl, InputLabel, MenuItem, Select } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +const elementOptions = [ + "Hydrogen", + "Helium", + "Lithium", + "Beryllium", + "Boron", + "Carbon", +]; + +type ExtraArgs = { + label?: string; + items?: string[]; +}; +type SelectArgs = React.ComponentProps & ExtraArgs; + +const meta: Meta = { + title: "MUI/Inputs/Select", + component: Select, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + variant: { control: "select", options: ["outlined", "filled", "standard"] }, + color: { + control: "select", + options: ["primary", "secondary", "success", "error", "info", "warning"], + }, + size: { control: "select", options: ["small", "medium"] }, + displayEmpty: { control: "boolean" }, + label: { control: "text" }, + items: { control: { type: "object" } }, + }, + args: { + variant: "outlined", + color: "primary", + size: "medium", + displayEmpty: false, + label: "element", + items: elementOptions, + defaultValue: elementOptions[0], + }, +}; +export default meta; +type Story = StoryObj; + +const renderSelectWithLabel = (args: SelectArgs) => ( + + {args.label && {args.label}} + + +); + +export const Basic: Story = { + render: (args: SelectArgs) => renderSelectWithLabel(args), +}; + +export const Multiple: Story = { + args: { + multiple: true, + defaultValue: [elementOptions[0], elementOptions[2]], + }, + render: (args: SelectArgs) => renderSelectWithLabel(args), +}; + +export const FilledAndStandard: Story = { + render: (args: SelectArgs) => ( + + {renderSelectWithLabel({ ...args, variant: "filled" })} + {renderSelectWithLabel({ ...args, variant: "standard" })} + + ), +}; diff --git a/src/components/MUI/Inputs/Slider.stories.tsx b/src/components/MUI/Inputs/Slider.stories.tsx new file mode 100644 index 0000000..7f29b6c --- /dev/null +++ b/src/components/MUI/Inputs/Slider.stories.tsx @@ -0,0 +1,61 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Box, Slider } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +const meta: Meta = { + title: "MUI/Inputs/Slider", + component: Slider, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + color: { + control: "select", + options: ["primary", "secondary", "success", "error", "info", "warning"], + }, + size: { control: "select", options: ["small", "medium"] }, + min: { control: { type: "number" } }, + max: { control: { type: "number" } }, + step: { control: { type: "number" } }, + marks: { control: "boolean" }, + valueLabelDisplay: { control: "select", options: ["off", "auto", "on"] }, + disabled: { control: "boolean" }, + }, + args: { + color: "primary", + size: "medium", + min: 0, + max: 100, + step: 1, + marks: false, + valueLabelDisplay: "auto", + disabled: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => , +}; + +export const DiscreteWithMarks: Story = { + args: { step: 10, marks: true, valueLabelDisplay: "auto" }, + render: (args) => , +}; + +export const Vertical: Story = { + args: { + orientation: "vertical", + min: 0, + max: 100, + step: 5, + marks: true, + valueLabelDisplay: "auto", + }, + render: (args) => ( + + + + ), +}; diff --git a/src/components/MUI/Inputs/Switch.stories.tsx b/src/components/MUI/Inputs/Switch.stories.tsx new file mode 100644 index 0000000..d6ebaf0 --- /dev/null +++ b/src/components/MUI/Inputs/Switch.stories.tsx @@ -0,0 +1,71 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import * as React from "react"; +import { Box, Switch } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +type SWArgs = React.ComponentProps; + +const meta: Meta = { + title: "MUI/Inputs/Switch", + component: Switch, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + color: { + control: "select", + options: [ + "default", + "primary", + "secondary", + "success", + "error", + "info", + "warning", + ], + }, + size: { control: "select", options: ["small", "medium"] }, + disabled: { control: "boolean" }, + edge: { control: "select", options: [false, "start", "end"] }, + }, + args: { + color: "primary", + size: "medium", + disabled: false, + edge: false, + }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => { + const [checked, setChecked] = React.useState(true); + return ( + setChecked(e.target.checked)} + /> + ); + }, +}; + +export const Colours: Story = { + render: (args) => ( + + + + + + + ), +}; + +export const Sizes: Story = { + render: (args) => ( + + + + + ), +}; diff --git a/src/components/MUI/Inputs/TextField.stories.tsx b/src/components/MUI/Inputs/TextField.stories.tsx new file mode 100644 index 0000000..32f1305 --- /dev/null +++ b/src/components/MUI/Inputs/TextField.stories.tsx @@ -0,0 +1,161 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Box, TextField } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +type TFArgs = React.ComponentProps; + +const meta: Meta = { + title: "MUI/Inputs/TextField", + component: TextField, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + variant: { + control: { type: "select" }, + options: ["outlined", "filled,", "standard"].map((s) => + s.replace(",", ""), + ), + }, + color: { + control: { type: "select" }, + options: ["primary", "secondary", "success", "error", "info", "warning"], + }, + size: { control: { type: "select" }, options: ["small", "medium"] }, + margin: { + control: { type: "select" }, + options: ["none", "dense", "normal"], + }, + type: { control: "text" }, + label: { control: "text" }, + placeholder: { control: "text" }, + helperText: { control: "text" }, + error: { control: "boolean" }, + required: { control: "boolean" }, + disabled: { control: "boolean" }, + fullWidth: { control: "boolean" }, + multiline: { control: "boolean" }, + rows: { control: { type: "number", min: 1, max: 12, step: 1 } }, + }, + args: { + variant: "outlined", + color: "primary", + size: "medium", + margin: "none", + type: "text", + label: "Your name", + placeholder: "Type here…", + helperText: "", + error: false, + required: false, + disabled: false, + fullWidth: false, + multiline: false, + rows: 1, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => , +}; + +export const Variants: Story = { + render: (args) => ( + + + + + + ), + args: { color: "primary", helperText: "" }, +}; + +export const SizesAndColors: Story = { + render: (args) => ( + + + + + + + + + + + ), + args: { variant: "outlined", helperText: "" }, +}; + +export const States: Story = { + render: (args) => ( + + + + + + + + + ), + args: { variant: "outlined", color: "primary" }, +}; + +export const Multiline: Story = { + args: { + multiline: true, + rows: 4, + label: "Message", + placeholder: "Write a few lines…", + }, + render: (args) => , +}; + +export const InputTypes: Story = { + render: (args) => ( + + + + + + + + ), + args: { variant: "outlined", helperText: "" }, +}; diff --git a/src/components/MUI/Inputs/ToggleButtonGroup.stories.tsx b/src/components/MUI/Inputs/ToggleButtonGroup.stories.tsx new file mode 100644 index 0000000..aec9f7f --- /dev/null +++ b/src/components/MUI/Inputs/ToggleButtonGroup.stories.tsx @@ -0,0 +1,74 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import * as React from "react"; +import { ToggleButton, ToggleButtonGroup } from "../MuiWrapped"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +type TBGArgs = React.ComponentProps; + +const meta: Meta = { + title: "MUI/Inputs/ToggleButtonGroup", + component: ToggleButtonGroup, + tags: ["autodocs"], + parameters: muiDocsParameters, + argTypes: { + color: { + control: "select", + options: [ + "standard", + "primary", + "secondary", + "success", + "error", + "info", + "warning", + ], + }, + size: { control: "select", options: ["small", "medium", "large"] }, + disabled: { control: "boolean" }, + orientation: { control: "select", options: ["horizontal", "vertical"] }, + }, + args: { + color: "primary", + size: "medium", + disabled: false, + orientation: "horizontal", + }, +}; +export default meta; +type Story = StoryObj; + +export const Exclusive: Story = { + args: { exclusive: true }, + render: (args) => { + const [value, setValue] = React.useState("left"); + return ( + setValue(next)} + > + Left + Center + Right + + ); + }, +}; + +export const Multiple: Story = { + args: { exclusive: false }, + render: (args) => { + const [value, setValue] = React.useState(["bold"]); + return ( + setValue(next)} + > + Bold + Italic + Underline + + ); + }, +}; diff --git a/src/components/MUI/Inputs/TransferList.stories.tsx b/src/components/MUI/Inputs/TransferList.stories.tsx new file mode 100644 index 0000000..d2878b3 --- /dev/null +++ b/src/components/MUI/Inputs/TransferList.stories.tsx @@ -0,0 +1,33 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { TransferList } from "./TransferList"; +import { muiDocsParameters } from "../../../../.storybook/muiDocsParameters"; + +const meta: Meta = { + title: "MUI/Inputs/TransferList", + component: TransferList, + tags: ["autodocs"], + parameters: muiDocsParameters, + args: { + left: [ + "Hydrogen", + "Helium", + "Lithium", + "Beryllium", + "Boron", + "Carbon", + "Nitrogen", + "Oxygen", + "Fluorine", + ], + right: ["Neon", "Sodium", "Magnesium"], + titleLeft: "Available", + titleRight: "Chosen", + }, +}; +export default meta; + +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => , +}; diff --git a/src/components/MUI/Inputs/TransferList.tsx b/src/components/MUI/Inputs/TransferList.tsx new file mode 100644 index 0000000..7fe399e --- /dev/null +++ b/src/components/MUI/Inputs/TransferList.tsx @@ -0,0 +1,107 @@ +import * as React from "react"; +import { + Button, + Checkbox, + List, + ListItem, + ListItemButton, + ListItemText, + Paper, +} from "../MuiWrapped"; + +export type TransferListProps = { + left?: string[]; + right?: string[]; + onChange?: (left: string[], right: string[]) => void; + titleLeft?: string; + titleRight?: string; +}; + +export const TransferList: React.FC = ({ + left = [], + right = [], + onChange, + titleLeft = "Available", + titleRight = "Selected", +}) => { + const [leftItems, setLeftItems] = React.useState(left); + const [rightItems, setRightItems] = React.useState(right); + const [checked, setChecked] = React.useState([]); + + const leftChecked = checked.filter((v) => leftItems.includes(v)); + const rightChecked = checked.filter((v) => rightItems.includes(v)); + + const toggle = (value: string) => { + setChecked((prev) => + prev.includes(value) + ? prev.filter((v) => v !== value) + : prev.concat(value), + ); + }; + + const moveRight = () => { + const nextRight = rightItems.concat(leftChecked); + const nextLeft = leftItems.filter((v) => !leftChecked.includes(v)); + setRightItems(nextRight); + setLeftItems(nextLeft); + setChecked((prev) => prev.filter((v) => !leftChecked.includes(v))); + onChange?.(nextLeft, nextRight); + }; + + const moveLeft = () => { + const nextLeft = leftItems.concat(rightChecked); + const nextRight = rightItems.filter((v) => !rightChecked.includes(v)); + setLeftItems(nextLeft); + setRightItems(nextRight); + setChecked((prev) => prev.filter((v) => !rightChecked.includes(v))); + onChange?.(nextLeft, nextRight); + }; + + const renderList = (title: string, items: string[]) => ( + +
+ {title} +
+ + {items.map((value) => { + const checkedItem = checked.includes(value); + return ( + + toggle(value)} dense> + + + + + ); + })} + +
+ ); + + return ( +
+ {renderList(titleLeft, leftItems)} +
+ + +
+ {renderList(titleRight, rightItems)} +
+ ); +}; + +TransferList.displayName = "TransferList"; diff --git a/src/components/MUI/MuiWrapped.tsx b/src/components/MUI/MuiWrapped.tsx index 06fef5a..67ada1b 100644 --- a/src/components/MUI/MuiWrapped.tsx +++ b/src/components/MUI/MuiWrapped.tsx @@ -53,6 +53,9 @@ import MuiBreadcrumbs, { BreadcrumbsProps as MuiBreadcrumbsProps, } from "@mui/material/Breadcrumbs"; import MuiButton, { ButtonProps as MuiButtonProps } from "@mui/material/Button"; +import MuiButtonGroup, { + ButtonGroupProps as MuiButtonGroupProps, +} from "@mui/material/ButtonGroup"; import MuiCard, { CardProps as MuiCardProps } from "@mui/material/Card"; import MuiCardActions, { CardActionsProps as MuiCardActionsProps, @@ -63,6 +66,9 @@ import MuiCardContent, { import MuiCardMedia, { CardMediaProps as MuiCardMediaProps, } from "@mui/material/CardMedia"; +import MuiCheckbox, { + CheckboxProps as MuiCheckboxProps, +} from "@mui/material/Checkbox"; import MuiChip, { ChipProps as MuiChipProps } from "@mui/material/Chip"; import MuiCircularProgress, { CircularProgressProps as MuiCircularProgressProps, @@ -81,6 +87,13 @@ import MuiDivider, { DividerProps as MuiDividerProps, } from "@mui/material/Divider"; import MuiDrawer, { DrawerProps as MuiDrawerProps } from "@mui/material/Drawer"; +import MuiFab, { FabProps as MuiFabProps } from "@mui/material/Fab"; +import MuiFormControl, { + FormControlProps as MuiFormControlProps, +} from "@mui/material/FormControl"; +import MuiFormControlLabel, { + FormControlLabelProps as MuiFormControlLabelProps, +} from "@mui/material/FormControlLabel"; import MuiIcon, { IconProps as MuiIconProps } from "@mui/material/Icon"; import MuiIconButton, { IconButtonProps as MuiIconButtonProps, @@ -89,6 +102,9 @@ import MuiLinearProgress, { LinearProgressProps as MuiLinearProgressProps, } from "@mui/material/LinearProgress"; import MuiLink, { LinkProps as MuiLinkProps } from "@mui/material/Link"; +import MuiInputLabel, { + InputLabelProps as MuiInputLabelProps, +} from "@mui/material/InputLabel"; import MuiList, { ListProps as MuiListProps } from "@mui/material/List"; import MuiListItem, { ListItemProps as MuiListItemProps, @@ -116,9 +132,16 @@ import MuiPagination, { PaginationProps as MuiPaginationProps, } from "@mui/material/Pagination"; import MuiPaper, { PaperProps as MuiPaperProps } from "@mui/material/Paper"; +import MuiRadio, { RadioProps as MuiRadioProps } from "@mui/material/Radio"; +import MuiRadioGroup, { + RadioGroupProps as MuiRadioGroupProps, +} from "@mui/material/RadioGroup"; +import MuiRating, { RatingProps as MuiRatingProps } from "@mui/material/Rating"; +import MuiSelect, { SelectProps as MuiSelectProps } from "@mui/material/Select"; import MuiSkeleton, { SkeletonProps as MuiSkeletonProps, } from "@mui/material/Skeleton"; +import MuiSlider, { SliderProps as MuiSliderProps } from "@mui/material/Slider"; import MuiSnackbar, { SnackbarProps as MuiSnackbarProps, } from "@mui/material/Snackbar"; @@ -144,6 +167,7 @@ import MuiSvgIcon, { SvgIconProps as MuiSvgIconProps, } from "@mui/material/SvgIcon"; import MuiTab, { TabProps as MuiTabProps } from "@mui/material/Tab"; +import MuiSwitch, { SwitchProps as MuiSwitchProps } from "@mui/material/Switch"; import MuiTable, { TableProps as MuiTableProps } from "@mui/material/Table"; import MuiTableBody, { TableBodyProps as MuiTableBodyProps, @@ -170,6 +194,15 @@ import MuiTableSortLabel, { TableSortLabelProps as MuiTableSortLabelProps, } from "@mui/material/TableSortLabel"; import MuiTabs, { TabsProps as MuiTabsProps } from "@mui/material/Tabs"; +import MuiTextField, { + TextFieldProps as MuiTextFieldProps, +} from "@mui/material/TextField"; +import MuiToggleButton, { + ToggleButtonProps as MuiToggleButtonProps, +} from "@mui/material/ToggleButton"; +import MuiToggleButtonGroup, { + ToggleButtonGroupProps as MuiToggleButtonGroupProps, +} from "@mui/material/ToggleButtonGroup"; import MuiToolbar, { ToolbarProps as MuiToolbarProps, } from "@mui/material/Toolbar"; @@ -306,6 +339,10 @@ export const Breadcrumbs = MuiWrapper( "Breadcrumbs", ); export const Button = MuiWrapper(MuiButton, "Button"); +export const ButtonGroup = MuiWrapper( + MuiButtonGroup, + "ButtonGroup", +); export const Card = MuiWrapper(MuiCard, "Card"); export const CardActions = MuiWrapper( MuiCardActions, @@ -320,6 +357,7 @@ export const CardMedia = MuiWrapper( "CardMedia", ); export const Chip = MuiWrapper(MuiChip, "Chip"); +export const Checkbox = MuiWrapper(MuiCheckbox, "Checkbox"); export const CircularProgress = MuiWrapper< MuiCircularProgressProps, HTMLSpanElement @@ -339,6 +377,15 @@ export const DialogTitle = MuiWrapper( ); export const Divider = MuiWrapper(MuiDivider, "Divider"); export const Drawer = MuiWrapper(MuiDrawer, "Drawer"); +export const Fab = MuiWrapper(MuiFab, "Fab"); +export const FormControl = MuiWrapper( + MuiFormControl, + "FormControl", +); +export const FormControlLabel = MuiWrapper( + MuiFormControlLabel, + "FormControlLabel", +); export const Icon = MuiWrapper(MuiIcon, "Icon"); export const IconButton = MuiWrapper( MuiIconButton, @@ -349,6 +396,10 @@ export const LinearProgress = MuiWrapper( "LinearProgress", ); export const Link = MuiWrapper(MuiLink, "Link"); +export const InputLabel = MuiWrapper( + MuiInputLabel, + "InputLabel", +); export const List = MuiWrapper(MuiList, "List"); export const ListItem = MuiWrapper(MuiListItem, "ListItem"); export const ListItemAvatar = MuiWrapper( @@ -378,7 +429,15 @@ export const Pagination = MuiWrapper( "Pagination", ); export const Paper = MuiWrapper(MuiPaper, "Paper"); +export const Radio = MuiWrapper(MuiRadio, "Radio"); +export const RadioGroup = MuiWrapper( + MuiRadioGroup, + "RadioGroup", +); +export const Rating = MuiWrapper(MuiRating, "Rating"); +export const Select = MuiWrapper(MuiSelect, "Select"); export const Skeleton = MuiWrapper(MuiSkeleton, "Skeleton"); +export const Slider = MuiWrapper(MuiSlider, "Slider"); export const Snackbar = MuiWrapper(MuiSnackbar, "Snackbar"); export const SpeedDial = MuiWrapper( MuiSpeedDial, @@ -401,6 +460,7 @@ export const StepContent = MuiWrapper( ); export const SvgIcon = MuiWrapper(MuiSvgIcon, "SvgIcon"); export const Tab = MuiWrapper(MuiTab, "Tab"); +export const Switch = MuiWrapper(MuiSwitch, "Switch"); export const Table = MuiWrapper(MuiTable, "Table"); export const TableBody = MuiWrapper( MuiTableBody, @@ -432,6 +492,18 @@ export const TableSortLabel = MuiWrapper( "TableSortLabel", ); export const Tabs = MuiWrapper(MuiTabs, "Tabs"); +export const TextField = MuiWrapper( + MuiTextField, + "TextField", +); +export const ToggleButton = MuiWrapper( + MuiToggleButton, + "ToggleButton", +); +export const ToggleButtonGroup = MuiWrapper( + MuiToggleButtonGroup, + "ToggleButtonGroup", +); export const Toolbar = MuiWrapper(MuiToolbar, "Toolbar"); export const Typography = MuiWrapper( MuiTypography,