i7-ui
v0.9.31
Published
A modern React UI component library built on top of Medusa UI, providing reusable components for building beautiful and accessible web applications.
Downloads
263
Readme
i7-UI
A modern React UI component library built on top of Medusa UI, providing reusable components for building beautiful and accessible web applications.
Features
- 🎨 Built on top of Medusa UI
- 🌙 Dark mode support
- ♿️ Accessible components following WCAG guidelines
- 📱 Responsive design
- 🎯 TypeScript support
- 🎄 Tree-shakeable
- 🎪 Built with TailwindCSS
Installation
Install i7-UI and its peer dependencies:
# Using npm
npm install i7-ui @medusajs/ui @medusajs/ui-preset tailwindcss
# Using yarn
yarn add i7-ui @medusajs/ui @medusajs/ui-preset tailwindcss
# Using pnpm
pnpm add i7-ui @medusajs/ui @medusajs/ui-preset tailwindcssSetup
- Add the Medusa UI preset to your
tailwind.config.ts:
import type { Config } from "tailwindcss";
import { join } from "path";
const config: Config = {
// for stop dark mode medusa js
darkMode: "class",
// eslint-disable-next-line @typescript-eslint/no-require-imports
presets: [require("@medusajs/ui-preset")],
content: [
join(__dirname, "src/**/!(*.stories|*.spec).{ts,tsx,html}"),
"./node_modules/@medusajs/ui/dist/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {
colors: {},
},
},
plugins: [],
};
export default config;Usage
Here's a quick example of using i7-UI components:
import { Select } from "@medusajs/ui";
function App() {
const currencies = [
{
value: "eur",
label: "EUR",
},
{
value: "usd",
label: "USD",
},
{
value: "dkk",
label: "DKK",
},
];
return (
<div>
<div className="w-[256px]">
<Select>
<Select.Trigger>
<Select.Value placeholder="Select a currency" />
</Select.Trigger>
<Select.Content>
{currencies.map((item) => (
<Select.Item key={item.value} value={item.value}>
{item.label}
</Select.Item>
))}
</Select.Content>
</Select>
</div>
</div>
);
}OTP Input
const App = () => {
const form = useForm<zod.infer<typeof CreateBranchesSchema>>({
defaultValues: {
name: "",
},
resolver: zodResolver(CreateBranchesSchema),
});
const [phoneNumber, setPhoneNumber] = useState("");
const handleSubmit = form.handleSubmit((data) => {
console.log(data);
});
console.log(phoneNumber);
return (
<div className="h-screen w-screen flex justify-center items-center">
<Form {...form}>
<KeyboundForm onSubmit={handleSubmit}>
<Form.Field
control={form.control}
name="name"
render={({ field }) => {
return (
<Form.Item>
<Form.Label>Name</Form.Label>
<Form.Control>
<OTP
{...field}
value={field.value}
onChange={(value) => {
field.onChange(value.toString());
setPhoneNumber(value.toString());
}}
numInputs={6}
shouldAutoFocus={true}
containerStyle={{
gap: "17px",
justifyContent: "center",
}}
inputStyle={{
width: "40px",
height: "40px",
border: "1px solid #aaa",
color: "#064552",
borderRadius: "10px",
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
);
}}
/>
<Button type="submit" className="mt-2">
Submit
</Button>
</KeyboundForm>
</Form>
</div>
);
};Action Menu Example
import { ActionMenu } from "@medusajs/ui";
function ActionMenuExample() {
return (
<ActionMenu>
<ActionMenu.Trigger>
<span>Options</span>
</ActionMenu.Trigger>
<ActionMenu.Content>
<ActionMenu.Item onClick={() => console.log("Edit")}>
Edit
</ActionMenu.Item>
<ActionMenu.Item onClick={() => console.log("Duplicate")}>
Duplicate
</ActionMenu.Item>
<ActionMenu.Separator />
<ActionMenu.Item
onClick={() => console.log("Delete")}
className="text-ui-danger"
>
Delete
</ActionMenu.Item>
</ActionMenu.Content>
</ActionMenu>
);
}Form with Custom Dropdown Example
const App = () => {
const form = useForm<zod.infer<typeof CreateBranchesSchema>>({
defaultValues: {
name: ["3", "5"],
},
resolver: zodResolver(CreateBranchesSchema),
});
const handleSubmit = form.handleSubmit((data) => {
console.log(data);
});
return (
<div className="h-screen w-screen flex justify-center items-center">
<Form {...form}>
<KeyboundForm onSubmit={handleSubmit}>
<Form.Field
control={form.control}
name="name"
render={({ field }) => {
return (
<Form.Item>
<Form.Label>Name</Form.Label>
<Form.Control>
<DropDown
className="w-[400px]"
{...field}
isMulti={true}
value={field.value}
onChange={(value) => {
field.onChange(value);
}}
options={[
{ label: "test1", value: "1" },
{ label: "test2", value: "2" },
{ label: "test3", value: "3" },
{ label: "test4", value: "4" },
{ label: "test5", value: "5" },
{ label: "test6", value: "6" },
{ label: "test7", value: "7" },
{ label: "test8", value: "8" },
{ label: "test9", value: "9" },
{ label: "test10", value: "10" },
{ label: "test11", value: "11" },
{ label: "test12", value: "12" },
{ label: "test13", value: "13" },
{ label: "test14", value: "14" },
]}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
);
}}
/>
<Button type="submit">Submit</Button>
</KeyboundForm>
</Form>
</div>
);
};Dropdown with mock api for support pagination
const CreateBranchesSchema = zod.object({
test: zod.array(zod.string()).min(1),
});
const options = [
{ label: "test1", value: "1" },
{ label: "test2", value: "2" },
{ label: "test3", value: "3" },
{ label: "test4", value: "4" },
{ label: "test5", value: "5" },
{ label: "test6", value: "6" },
{ label: "test7", value: "7" },
{ label: "test8", value: "8" },
{ label: "test9", value: "9" },
{ label: "test10", value: "10" },
{ label: "test11", value: "11" },
{ label: "test12", value: "12" },
{ label: "test13", value: "13" },
{ label: "test14", value: "14" },
{ label: "test15", value: "15" },
{ label: "test16", value: "16" },
{ label: "test17", value: "17" },
{ label: "test18", value: "18" },
{ label: "test19", value: "19" },
{ label: "test20", value: "20" },
{ label: "test21", value: "21" },
{ label: "test22", value: "22" },
{ label: "test23", value: "23" },
{ label: "test24", value: "24" },
{ label: "test25", value: "25" },
{ label: "test26", value: "26" },
{ label: "test27", value: "27" },
{ label: "test28", value: "28" },
{ label: "test29", value: "29" },
{ label: "test30", value: "30" },
{ label: "test31", value: "31" },
{ label: "test32", value: "32" },
{ label: "test33", value: "33" },
{ label: "test34", value: "34" },
{ label: "test35", value: "35" },
{ label: "test36", value: "36" },
{ label: "test37", value: "37" },
{ label: "test38", value: "38" },
{ label: "test39", value: "39" },
{ label: "test40", value: "40" },
];
const mockApiCall = (delayMs: number, page: number): Promise<any> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, delayMs);
});
};
const App = () => {
const form = useForm<zod.infer<typeof CreateBranchesSchema>>({
defaultValues: {
test: ["32", "33"],
},
resolver: zodResolver(CreateBranchesSchema),
});
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement | null>(null);
const onSubmit = (data: zod.infer<typeof CreateBranchesSchema>) => {
console.log(data);
};
const totalPages = 4;
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const [optionsData, setOptionsData] = useState<any[]>([]);
useEffect(() => {
const test = async () => {
setLoading(true);
await mockApiCall(3000, page);
setOptionsData(options.slice(0, page * 10));
setLoading(false);
};
test();
}, [page]);
return (
<div className="h-screen w-screen flex justify-center items-center">
<Form {...form}>
<KeyboundForm onSubmit={form.handleSubmit(onSubmit)}>
<Form.Field
control={form.control}
name="test"
render={({ field }) => {
return (
<Form.Item>
<Form.Label>Name</Form.Label>
<Form.Control>
<DropDown
className="w-[400px]"
{...field}
// isLoading={loading}
isMulti={true}
placeholder="City"
value={field.value}
// initialValuesSelected={[
// { label: "test32", value: "32" },
// { label: "test33", value: "33" },
// ]}
onChange={(value) => {
field.onChange(value);
}}
// options={optionsData}
options={options}
// onScrollBottom={async (entry) => {
// if (
// entry.isIntersecting &&
// page <= totalPages &&
// !loading
// ) {
// setPage((prev) => Math.min(prev + 1, totalPages));
// }
// }}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
);
}}
/>
<Button type="submit">Submit</Button>
</KeyboundForm>
</Form>
</div>
);
};
export default App;Image Upload Example
You can use the ImageUpload component to upload an image.
use useUpload hook to upload the image to the server.
You can use this component with react-hook-form or formik.
const App = () => {
const form = useForm<zod.infer<typeof CreateBranchesSchema>>({
defaultValues: {
name: faker.image.avatar(),
},
resolver: zodResolver(CreateBranchesSchema),
});
const handleSubmit = form.handleSubmit((data) => {
console.log(data);
});
const handleUploadFile = async (
e: React.ChangeEvent<HTMLInputElement>,
field: any
) => {
const files = e.target.files;
if (files && files.length > 0) {
const file = new FormData();
file.append("file", files[0]);
field.onChange(faker.image.avatar());
}
};
return (
<div className="h-screen w-screen flex justify-center items-center">
<Form {...form}>
<KeyboundForm onSubmit={handleSubmit}>
<Form.Field
control={form.control}
name="name"
render={({ field }) => {
return (
<Form.Item>
<Form.Label>Name</Form.Label>
<Form.Control>
<ImageUpload
value={field.value}
onChange={(e) => handleUploadFile(e, field)}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
);
}}
/>
<Button type="submit">Submit</Button>
</KeyboundForm>
</Form>
</div>
);
};DndFile Example
You can use the DndFile component to upload a file or multiple files here is the example of multiple files.
use useUpload hook to upload the file to the server.
You can use this component with react-hook-form or formik.
const App = () => {
const [files, setFiles] = useState<File[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<FileRejection[]>([]);
const form = useForm<zod.infer<typeof CreateBranchesSchema>>({
defaultValues: {
url: [faker.image.avatar(), faker.image.avatar()],
},
resolver: zodResolver(CreateBranchesSchema),
});
const handleSubmit = form.handleSubmit((data) => {
console.log(data);
});
const onDrop = useCallback(
async (acceptedFiles: any, rejectedFiles: any, field: any) => {
if (rejectedFiles.length > 0) {
setRejectedFiles(rejectedFiles);
return;
}
setFiles(acceptedFiles);
field.onChange([
...(Array.isArray(field.value) ? field.value : []),
...acceptedFiles.map((item: any) => faker.image.avatar()),
]);
},
[]
);
return (
<div className="h-screen w-screen flex justify-center items-center">
<Form {...form}>
<KeyboundForm onSubmit={handleSubmit}>
<Form.Field
control={form.control}
name="url"
render={({ field }) => {
return (
<Form.Item>
<Form.Label>Url</Form.Label>
<Form.Control>
<DndFile
value={field.value}
multiFiles={true}
onDrop={(acceptedFiles, rejectedFiles) =>
onDrop(acceptedFiles, rejectedFiles, field)
}
onChange={(values) => field.onChange(values)}
uploadOptions={{
maxFiles: 3,
}}
uploadFilesInfo={{
progress: 50,
files: files,
rejectedFiles: rejectedFiles,
}}
fileCount={true}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
);
}}
/>
<Button type="submit" className="mt-2">
Submit
</Button>
</KeyboundForm>
</Form>
</div>
);
};Modal i7-ui
<Modal
open={open}
bodyClassName="w-[50%] min-h-[500px] bg-white"
// for custom click outside
// handleClickOutside={() => {
// const parent = ref.current?.parentElement;
// if (parent?.classList.contains("confirm")) {
// parent.classList.remove("enter");
// parent.classList.remove("confirm");
// setTimeout(() => {
// parent?.classList.add("confirm");
// }, 10);
// } else {
// parent?.classList.add("confirm");
// }
// }}
>
<div ref={ref} className="w-full h-full bg-white">
<Modal.ModalHeader className=" bg-red-500">test</Modal.ModalHeader>
<Modal.ModalBody className=" bg-green-500">body</Modal.ModalBody>
<Modal.ModalFooter className=" bg-blue-500">footer</Modal.ModalFooter>
</div>
</Modal>Hooks
useComboboxData
The useComboboxData hook is used when implementing a combobox component that needs to fetch data from an API. It provides functionality for handling search, pagination, and initial data loading.
npm install @tanstack/react-query axiosAvailable Components
i7-UI provides a wide range of components including:
- ActionMenu
- Combobox
- Dropdown
- Form
- KeyboundForm
- Modal
- Dropdown
- Container
- Header
- GridLayout , GridItem
- SectionRow
- Skeleton
- SingleColumnLayout
- TwoColumnLayout
- Pagination
- Table
- ImageUpload
- DndFile For detailed documentation of each component, please visit our documentation site (coming soon).
Contributing
We welcome contributions! Please read our contributing guidelines (coming soon) to get started.
License
MIT © [i7-UI]
Links
Support
If you need help or have questions:
