@cwncollab-org/component-kit v0.4.7
Component Kit
Note: This documentation was generated with the assistance of AI. While we strive for accuracy, please verify any code examples or implementation details in your specific use case.
A React component library built with TypeScript and Vite. This package provides a set of reusable components built on top of Material-UI (MUI) and Tanstack Form for form handling.
Features
- Built on Material-UI
- Type-safe dialog management
- Lazy loading support
- Payload and result handling for dialogs
- Form components with TanStack Form integration
Usage Examples
Basic Dialog Usage
import { DialogsProvider, useDialogs } from '@cwncollab-org/component-kit'
import ExampleDialog from './examples/ExampleDialog'
function App() {
return (
<DialogsProvider>
<AppPage />
</DialogsProvider>
)
}
function AppPage() {
const { openDialog } = useDialogs()
return (
<Button
variant='contained'
onClick={() => openDialog('example-dialog', ExampleDialog)}
>
Open Basic Dialog
</Button>
)
}ExampleDialog Implementation
import {
Dialog,
DialogTitle,
DialogContentText,
DialogContent,
DialogProps,
} from '@mui/material'
export default function ExampleDialog({ open, onClose, ...rest }: DialogProps) {
return (
<Dialog open={open} onClose={onClose} {...rest}>
<DialogTitle>Example Dialog</DialogTitle>
<DialogContent>
<DialogContentText>This is an example dialog</DialogContentText>
</DialogContent>
</Dialog>
)
}Dialog with Payload
import { DialogsProvider, useDialogs } from '@cwncollab-org/component-kit'
import ExampleDialogWithPayload from './examples/ExampleDialogWithPayload'
function AppPage() {
const { openDialog } = useDialogs()
const [name, setName] = useState('')
return (
<>
<Input
value={name}
onChange={e => setName(e.target.value)}
placeholder='Enter your name'
/>
<Button
variant='contained'
onClick={() => {
openDialog(
'example-dialog-with-payload',
ExampleDialogWithPayload,
{ payload: { name } }
)
}}
>
Open Dialog with Payload
</Button>
</>
)
}ExampleDialogWithPayload Implementation
import {
Dialog,
DialogTitle,
DialogContentText,
DialogContent,
} from '@mui/material'
import { DialogProps } from '../lib'
type Payload = {
name: string
}
export default function ExampleDialogWithPayload({
open,
onClose,
payload,
...rest
}: DialogProps<Payload>) {
return (
<Dialog open={open} onClose={onClose} {...rest}>
<DialogTitle>Example Dialog</DialogTitle>
<DialogContent>
<DialogContentText>
This is an example dialog with payload {JSON.stringify(payload)}
</DialogContentText>
</DialogContent>
</Dialog>
)
}Dialog with Result
import { DialogsProvider, useDialogs } from '@cwncollab-org/component-kit'
import ExampleDialogWithResult from './examples/ExampleDialogWithResult'
function AppPage() {
const { openDialog } = useDialogs()
const [result, setResult] = useState<{ name: string } | null>(null)
return (
<>
<Button
variant='contained'
onClick={async () => {
const result = await openDialog(
'example-dialog-with-result',
ExampleDialogWithResult,
{ payload: { name: 'Initial Name' } }
)
if (result?.success) {
setResult(result.data)
}
}}
>
Open Dialog with Result
</Button>
{result && (
<Typography variant='body1'>
Result: {result.name}
</Typography>
)}
</>
)
}ExampleDialogWithResult Implementation
import {
Dialog,
DialogTitle,
DialogContent,
Input,
DialogActions,
Button,
} from '@mui/material'
import { DialogProps } from '../lib'
import { useState } from 'react'
type Payload = {
name: string
}
type ResultData = {
name: string
}
export default function ExampleDialogWithResult({
open,
onClose,
payload,
...rest
}: DialogProps<Payload, ResultData>) {
const [name, setName] = useState(payload?.name || '')
return (
<Dialog open={open} onClose={onClose} {...rest}>
<DialogTitle>Example Dialog</DialogTitle>
<DialogContent>
<Input
value={name}
onChange={e => setName(e.target.value)}
placeholder='Name'
/>
</DialogContent>
<DialogActions>
<Button onClick={ev => onClose(ev, { success: true, data: { name } })}>
Save
</Button>
</DialogActions>
</Dialog>
)
}Lazy Loading Dialog
import { DialogsProvider, useDialogs } from '@cwncollab-org/component-kit'
import { lazy } from 'react'
const ExampleDialog2 = lazy(() => import('./examples/ExampleDialog2'))
function AppPage() {
const { openDialog } = useDialogs()
return (
<Button
variant='contained'
onClick={() => openDialog('example-dialog2', ExampleDialog2)}
>
Open Lazy Loaded Dialog
</Button>
)
}ExampleDialog2 Implementation
import {
Dialog,
DialogTitle,
DialogContentText,
DialogContent,
DialogProps,
} from '@mui/material'
export default function ExampleDialog2({
open,
onClose,
...rest
}: DialogProps) {
return (
<Dialog open={open} onClose={onClose} {...rest}>
<DialogTitle>Example Dialog</DialogTitle>
<DialogContent>
<DialogContentText>This is an example dialog 2</DialogContentText>
</DialogContent>
</Dialog>
)
}Form Example with Zod Validation
import { Box, Button, Paper, Stack, Typography } from '@mui/material'
import { useAppForm } from '@cwncollab-org/component-kit'
import { useState } from 'react'
import { z } from 'zod'
// Define your form schema with Zod
const formSchema = z.object({
username: z.string()
.min(1, 'Username is required')
.max(20, 'Username must be less than 20 characters')
.regex(/^[a-zA-Z0-9_]+$/, 'Username can only contain letters, numbers, and underscores'),
email: z.string()
.email('Invalid email address')
.min(1, 'Email is required'),
age: z.number()
.min(18, 'You must be at least 18 years old')
.max(120, 'Please enter a valid age'),
subscribe: z.boolean()
.refine(val => val === true, 'You must subscribe to continue'),
role: z.string()
.min(1, 'Please select a role'),
birthDate: z.date()
.min(new Date('1900-01-01'), 'Please enter a valid birth date')
.max(new Date(), 'Birth date cannot be in the future'),
})
// Infer the form type from the schema
type FormValues = z.infer<typeof formSchema>
export function FormExample() {
const [value, setValue] = useState<FormValues>({
username: '',
email: '',
age: 18,
subscribe: false,
role: '',
birthDate: new Date(),
})
const form = useAppForm({
defaultValues: {
username: '',
email: '',
age: 18,
subscribe: false,
role: '',
birthDate: new Date(),
},
onSubmit: ({ value }) => {
setValue(value as FormValues)
},
// Add Zod validation
validators: { onChange: formSchema },
})
return (
<Paper sx={{ p: 2 }}>
<Box>
<Typography>Form Example with Zod Validation</Typography>
</Box>
<Box
component='form'
sx={{ py: 2 }}
onSubmit={e => {
e.preventDefault()
e.stopPropagation()
form.handleSubmit()
}}
>
<Stack spacing={2}>
<form.AppField
name='username'
children={field => (
<field.TextField
label='Username'
fullWidth
/>
)}
/>
<form.AppField
name='email'
children={field => (
<field.TextField
label='Email'
fullWidth
/>
)}
/>
<form.AppField
name='age'
children={field => (
<field.TextField
label='Age'
type='number'
fullWidth
/>
)}
/>
<form.AppField
name='birthDate'
children={field => (
<field.DatePicker
label='Birth Date'
/>
)}
/>
<form.AppField
name='role'
children={field => (
<field.Select
label='Role'
fullWidth
>
<MenuItem value=''>
<em>None</em>
</MenuItem>
<MenuItem value='admin'>Admin</MenuItem>
<MenuItem value='user'>User</MenuItem>
</field.Select>
)}
/>
<form.AppField
name='subscribe'
children={field => (
<field.Checkbox
label='Subscribe to newsletter'
/>
)}
/>
<form.AppForm>
<form.SubscribeButton type='submit' variant='contained'>
Submit
</form.SubscribeButton>
<form.SubscribeButton
type='reset'
variant='outlined'
onClick={() => form.reset()}
>
Reset
</form.SubscribeButton>
</form.AppForm>
<Typography variant='body1'>{JSON.stringify(value)}</Typography>
</Stack>
</Box>
</Paper>
)
}Layout Components
The library provides a flexible and responsive layout system built on top of Material-UI. The main layout component is AppLayout which includes a responsive app bar, collapsible sidebar, and main content area.
Basic Layout Usage
import { AppLayout, NavList } from '@cwncollab-org/component-kit'
import { Home, Article, Person } from '@mui/icons-material'
import { IconButton } from '@mui/material'
// Define navigation items
const navList: NavList = {
items: [
{
icon: <Home />,
label: 'Home',
url: '/',
},
{
icon: <Article />,
label: 'Form Example',
url: '/form-example',
},
{
icon: <Article />,
label: 'Dialogs Example',
url: '/dialogs-example',
},
],
}
function App() {
return (
<AppLayout
title='Demo App'
navList={navList}
slotProps={{
appBar: {
endSlot: (
<IconButton color='inherit' aria-label='profile'>
<Person />
</IconButton>
),
},
}}
>
{/* Your app content goes here */}
<div>Main Content</div>
</AppLayout>
)
}Layout Features
- Responsive design with mobile-friendly drawer
- Collapsible sidebar with smooth transitions
- Customizable app bar with optional end slot
- Type-safe navigation list configuration
- Support for multiple navigation lists
- Customizable drawer widths for expanded and collapsed states
Layout Props
type AppLayoutProps = {
title?: string // App bar title
navList?: NavList | NavList[] // Navigation items
drawerWidth?: number // Width of expanded drawer (default: 240)
collapsedDrawerWidth?: number // Width of collapsed drawer (default: 64)
slotProps?: {
appBar?: Omit<AppBarProps, 'title' | 'status' | 'sidebarOpen' | 'drawerWidth' | 'collapsedDrawerWidth' | 'onDrawerToggle'>
}
sx?: SxProps // Custom styles
children: React.ReactNode // Main content
}Navigation List Configuration
type NavList = {
items: Array<{
icon: React.ReactNode // Navigation item icon
label: string // Navigation item label
url?: string // Optional URL for navigation
}>
}MultiSelect Component
The MultiSelect component provides a multiple selection dropdown with checkboxes, built on top of MUI's Select component and integrated with TanStack Form.
import { useAppForm } from '@cwncollab-org/component-kit'
import { z } from 'zod'
// Define your form schema
const formSchema = z.object({
categories: z.array(z.string()).min(1, 'Please select at least one category'),
})
// Define your options
const categories = [
{ value: 'tech', label: 'Technology' },
{ value: 'sports', label: 'Sports' },
{ value: 'news', label: 'News' },
{ value: 'entertainment', label: 'Entertainment' },
{ value: 'science', label: 'Science' },
]
function MyForm() {
const form = useAppForm({
defaultValues: {
categories: [],
},
validators: {
onSubmit: formSchema,
},
onSubmit: ({ value }) => {
console.log('Selected categories:', value.categories)
},
})
return (
<form.AppField
name="categories"
children={field => (
<field.MultiSelect
label="Categories"
required
options={categories}
labelShrink
size="small"
fullWidth
// Optional: Sort selected values by label or value
sortSelected="label"
/>
)}
/>
)
}MultiSelect Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | - | The label text for the select field |
labelShrink | boolean | false | Whether the label should shrink |
size | 'small' \| 'medium' | 'medium' | The size of the select field |
fullWidth | boolean | false | Whether the select should take full width |
options | Array<{ value: string, label: string }> \| string[] | [] | The options to display in the select |
sortSelected | 'label' \| 'value' \| false | false | Sort selected values by label or value |
slotProps | object | - | Props for underlying MUI components |
Common Dialog Patterns
Here are some common dialog patterns you can implement using the component kit:
Confirmation Dialog
import { useConfirmDialog } from '@cwncollab-org/component-kit'
function MyComponent() {
const confirm = useConfirmDialog()
const handleClick = async () => {
const result = await confirm({
title: 'Confirm',
message: 'Are you sure you want to add this item to cart?',
})
if (result) {
// Proceed
}
}
return (
<Button onClick={handleClick}>
Click
</Button>
)
}Delete Confirmation Dialog
import { useConfirmDeleteDialog } from '@cwncollab-org/component-kit'
function MyComponent() {
const confirmDelete = useConfirmDeleteDialog()
const handleDelete = async () => {
const result = await confirmDelete({
title: 'Delete Item',
message: 'Are you sure you want to delete this item? This action cannot be undone.',
})
if (result) {
// Proceed with deletion
}
}
return (
<Button onClick={handleDelete}>
Delete Item
</Button>
)
}9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago