@delightui/components 0.1.105 → 0.1.107
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -1
- package/dist/cjs/components/molecules/Modal/DemoModal.d.ts +8 -0
- package/dist/cjs/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
- package/dist/cjs/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
- package/dist/cjs/components/molecules/Modal/ModalContext/index.d.ts +3 -0
- package/dist/cjs/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
- package/dist/cjs/components/molecules/Modal/index.d.ts +2 -0
- package/dist/cjs/components/molecules/index.d.ts +2 -0
- package/dist/cjs/library.css +19 -6
- package/dist/cjs/library.js +3 -3
- package/dist/cjs/library.js.map +1 -1
- package/dist/esm/components/molecules/Modal/DemoModal.d.ts +8 -0
- package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
- package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
- package/dist/esm/components/molecules/Modal/ModalContext/index.d.ts +3 -0
- package/dist/esm/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
- package/dist/esm/components/molecules/Modal/index.d.ts +2 -0
- package/dist/esm/components/molecules/index.d.ts +2 -0
- package/dist/esm/library.css +19 -6
- package/dist/esm/library.js +3 -3
- package/dist/esm/library.js.map +1 -1
- package/dist/index.d.ts +108 -2
- package/docs/README.md +264 -0
- package/docs/components/atoms/ActionImage.md +119 -0
- package/docs/components/atoms/Button.md +197 -0
- package/docs/components/atoms/Checkbox.md +299 -0
- package/docs/components/atoms/CheckboxItem.md +314 -0
- package/docs/components/atoms/Chip.md +380 -0
- package/docs/components/atoms/CustomToggle.md +270 -0
- package/docs/components/atoms/Icon.md +365 -0
- package/docs/components/atoms/IconButton.md +407 -0
- package/docs/components/atoms/Image.md +448 -0
- package/docs/components/atoms/Input.md +430 -0
- package/docs/components/atoms/ListItem.md +502 -0
- package/docs/components/atoms/Password.md +472 -0
- package/docs/components/atoms/RadioButton.md +614 -0
- package/docs/components/atoms/RadioButtonItem.md +588 -0
- package/docs/components/atoms/ResponsiveComponent.md +612 -0
- package/docs/components/atoms/SelectListItem.md +609 -0
- package/docs/components/atoms/Slider.md +605 -0
- package/docs/components/atoms/Spinner.md +605 -0
- package/docs/components/atoms/Text.md +463 -0
- package/docs/components/atoms/TextArea.md +670 -0
- package/docs/components/atoms/ToastNotification.md +668 -0
- package/docs/components/atoms/Toggle.md +737 -0
- package/docs/components/atoms/ToggleButton.md +751 -0
- package/docs/components/atoms/Tooltip.md +391 -0
- package/docs/components/molecules/Accordion.md +440 -0
- package/docs/components/molecules/AccordionGroup.md +547 -0
- package/docs/components/molecules/ActionCard.md +546 -0
- package/docs/components/molecules/Breadcrumb.md +403 -0
- package/docs/components/molecules/Breadcrumbs.md +485 -0
- package/docs/components/molecules/ButtonGroup.md +383 -0
- package/docs/components/molecules/Card.md +298 -0
- package/docs/components/molecules/ChipInput.md +646 -0
- package/docs/components/molecules/ContextMenu.md +768 -0
- package/docs/components/molecules/CustomTimeSelector.md +116 -0
- package/docs/components/molecules/DatePicker.md +516 -0
- package/docs/components/molecules/DateTimeSelector.md +166 -0
- package/docs/components/molecules/FormField.md +312 -0
- package/docs/components/molecules/Grid.md +577 -0
- package/docs/components/molecules/GridItem.md +834 -0
- package/docs/components/molecules/GridList.md +244 -0
- package/docs/components/molecules/List.md +485 -0
- package/docs/components/molecules/Modal.md +470 -0
- package/docs/components/molecules/ModalFooter.md +702 -0
- package/docs/components/molecules/ModalHeader.md +756 -0
- package/docs/components/molecules/ModalProvider.md +205 -0
- package/docs/components/molecules/Nav.md +530 -0
- package/docs/components/molecules/NavItem.md +572 -0
- package/docs/components/molecules/NavLink.md +499 -0
- package/docs/components/molecules/Option.md +521 -0
- package/docs/components/molecules/Pagination.md +592 -0
- package/docs/components/molecules/PaginationNumberField.md +722 -0
- package/docs/components/molecules/Popover.md +516 -0
- package/docs/components/molecules/ProgressBar.md +624 -0
- package/docs/components/molecules/RadioGroup.md +831 -0
- package/docs/components/molecules/RepeaterList.md +185 -0
- package/docs/components/molecules/Select.md +402 -0
- package/docs/components/molecules/SortableTrigger.md +82 -0
- package/docs/components/molecules/useModal.md +379 -0
- package/docs/components/organisms/Dropzone.md +346 -0
- package/docs/components/organisms/DropzoneClear.md +135 -0
- package/docs/components/organisms/DropzoneContent.md +216 -0
- package/docs/components/organisms/DropzoneFilename.md +191 -0
- package/docs/components/organisms/DropzoneSupportedFormats.md +184 -0
- package/docs/components/organisms/DropzoneTrigger.md +209 -0
- package/docs/components/organisms/Form.md +533 -0
- package/docs/components/organisms/SlideOutPanel.md +662 -0
- package/docs/components/organisms/TabContent.md +902 -0
- package/docs/components/organisms/TabItem.md +1091 -0
- package/docs/components/organisms/Table.md +611 -0
- package/docs/components/organisms/TableBody.md +679 -0
- package/docs/components/organisms/TableCell.md +482 -0
- package/docs/components/organisms/TableHeader.md +513 -0
- package/docs/components/organisms/TableHeaderCell.md +661 -0
- package/docs/components/organisms/TableRow.md +715 -0
- package/docs/components/organisms/Tabs.md +1330 -0
- package/docs/components/utils/ConditionalView.md +568 -0
- package/docs/components/utils/RenderStateView.md +726 -0
- package/docs/components/utils/WrapTextNodes.md +614 -0
- package/package.json +3 -2
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# RepeaterList
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
A simple list rendering component that iterates over an array of data and renders each item using a specified component. It provides a minimal, lightweight approach to rendering collections without additional layout or styling, making it perfect for simple repetitive content rendering.
|
|
6
|
+
|
|
7
|
+
## Aliases
|
|
8
|
+
|
|
9
|
+
- RepeaterList
|
|
10
|
+
- ItemRepeater
|
|
11
|
+
- DataRepeater
|
|
12
|
+
- ComponentRepeater
|
|
13
|
+
|
|
14
|
+
## Props Breakdown
|
|
15
|
+
|
|
16
|
+
| Prop | Type | Default | Required | Description |
|
|
17
|
+
|------|------|---------|----------|-------------|
|
|
18
|
+
| `data` | `T[]` | - | Yes | Array of data items to render |
|
|
19
|
+
| `component` | `React.ComponentType<T>` | - | Yes | Component to render for each data item |
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
### Basic Usage
|
|
24
|
+
```tsx
|
|
25
|
+
import { RepeaterList } from '@delightui/components';
|
|
26
|
+
|
|
27
|
+
const ListItem = ({ name, email }) => (
|
|
28
|
+
<div className="user-item">
|
|
29
|
+
<span>{name}</span>
|
|
30
|
+
<span>{email}</span>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
function BasicRepeaterExample() {
|
|
35
|
+
const users = [
|
|
36
|
+
{ name: 'John Doe', email: 'john@example.com' },
|
|
37
|
+
{ name: 'Jane Smith', email: 'jane@example.com' },
|
|
38
|
+
{ name: 'Bob Johnson', email: 'bob@example.com' },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="user-list">
|
|
43
|
+
<RepeaterList data={users} component={ListItem} />
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Simple Text List
|
|
50
|
+
```tsx
|
|
51
|
+
function SimpleTextExample() {
|
|
52
|
+
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];
|
|
53
|
+
|
|
54
|
+
const TextItem = ({ children }) => (
|
|
55
|
+
<li className="fruit-item">{children}</li>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Transform strings to objects for the component
|
|
59
|
+
const data = items.map(item => ({ children: item }));
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<ul className="fruit-list">
|
|
63
|
+
<RepeaterList data={data} component={TextItem} />
|
|
64
|
+
</ul>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Card Repeater
|
|
70
|
+
```tsx
|
|
71
|
+
import { Card, Text, Button } from '@delightui/components';
|
|
72
|
+
|
|
73
|
+
function CardRepeaterExample() {
|
|
74
|
+
const products = [
|
|
75
|
+
{ id: 1, name: 'Laptop', price: 999, description: 'High-performance laptop' },
|
|
76
|
+
{ id: 2, name: 'Phone', price: 699, description: 'Latest smartphone' },
|
|
77
|
+
{ id: 3, name: 'Tablet', price: 399, description: 'Portable tablet device' },
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const ProductCard = ({ name, price, description, onAddToCart }) => (
|
|
81
|
+
<Card className="product-card">
|
|
82
|
+
<Text type="Heading" size="Medium">{name}</Text>
|
|
83
|
+
<Text type="Body" color="Secondary">{description}</Text>
|
|
84
|
+
<Text type="Heading" size="Small">${price}</Text>
|
|
85
|
+
<Button onClick={() => onAddToCart(id)}>Add to Cart</Button>
|
|
86
|
+
</Card>
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const handleAddToCart = (productId) => {
|
|
90
|
+
console.log('Added to cart:', productId);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div className="product-grid">
|
|
95
|
+
<RepeaterList
|
|
96
|
+
data={products.map(product => ({
|
|
97
|
+
...product,
|
|
98
|
+
onAddToCart: handleAddToCart
|
|
99
|
+
}))}
|
|
100
|
+
component={ProductCard}
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Navigation Menu
|
|
108
|
+
```tsx
|
|
109
|
+
function NavigationExample() {
|
|
110
|
+
const menuItems = [
|
|
111
|
+
{ label: 'Home', href: '/', icon: 'Home' },
|
|
112
|
+
{ label: 'Products', href: '/products', icon: 'ShoppingBag' },
|
|
113
|
+
{ label: 'About', href: '/about', icon: 'Info' },
|
|
114
|
+
{ label: 'Contact', href: '/contact', icon: 'Mail' },
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
const MenuItem = ({ label, href, icon }) => (
|
|
118
|
+
<a href={href} className="menu-item">
|
|
119
|
+
<Icon icon={icon} size="Small" />
|
|
120
|
+
<span>{label}</span>
|
|
121
|
+
</a>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<nav className="navigation">
|
|
126
|
+
<RepeaterList data={menuItems} component={MenuItem} />
|
|
127
|
+
</nav>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Status List
|
|
133
|
+
```tsx
|
|
134
|
+
function StatusListExample() {
|
|
135
|
+
const statuses = [
|
|
136
|
+
{ id: 1, message: 'System is running normally', type: 'success', timestamp: '2024-01-15 10:30:00' },
|
|
137
|
+
{ id: 2, message: 'Minor delay in processing', type: 'warning', timestamp: '2024-01-15 10:25:00' },
|
|
138
|
+
{ id: 3, message: 'Maintenance completed', type: 'info', timestamp: '2024-01-15 10:20:00' },
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
const StatusItem = ({ message, type, timestamp }) => (
|
|
142
|
+
<div className={`status-item status-${type}`}>
|
|
143
|
+
<div className="status-message">{message}</div>
|
|
144
|
+
<div className="status-timestamp">{timestamp}</div>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div className="status-feed">
|
|
150
|
+
<RepeaterList data={statuses} component={StatusItem} />
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Comment Thread
|
|
157
|
+
```tsx
|
|
158
|
+
function CommentThreadExample() {
|
|
159
|
+
const comments = [
|
|
160
|
+
{ id: 1, author: 'Alice', content: 'Great post!', likes: 5, timestamp: '2 hours ago' },
|
|
161
|
+
{ id: 2, author: 'Bob', content: 'Thanks for sharing this.', likes: 2, timestamp: '1 hour ago' },
|
|
162
|
+
{ id: 3, author: 'Charlie', content: 'Very informative.', likes: 8, timestamp: '30 minutes ago' },
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
const CommentItem = ({ author, content, likes, timestamp }) => (
|
|
166
|
+
<div className="comment">
|
|
167
|
+
<div className="comment-header">
|
|
168
|
+
<strong>{author}</strong>
|
|
169
|
+
<span className="comment-time">{timestamp}</span>
|
|
170
|
+
</div>
|
|
171
|
+
<div className="comment-content">{content}</div>
|
|
172
|
+
<div className="comment-actions">
|
|
173
|
+
<button className="like-button">👍 {likes}</button>
|
|
174
|
+
<button className="reply-button">Reply</button>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<div className="comment-thread">
|
|
181
|
+
<RepeaterList data={comments} component={CommentItem} />
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
# Select
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
A dropdown selection component that allows users to choose from a list of options. Supports single and multiple selections, custom icons, placeholder text, and integrates seamlessly with form validation. Built with accessibility and keyboard navigation in mind.
|
|
6
|
+
|
|
7
|
+
## Aliases
|
|
8
|
+
|
|
9
|
+
- Select
|
|
10
|
+
- Dropdown
|
|
11
|
+
- ComboBox
|
|
12
|
+
- Picker
|
|
13
|
+
- OptionList
|
|
14
|
+
|
|
15
|
+
## Props Breakdown
|
|
16
|
+
|
|
17
|
+
**Extends:** `HTMLAttributes<HTMLElement>` (excluding `style`, `children`, `onClick`) + `ControlledFormComponentProps<FieldValue>`
|
|
18
|
+
|
|
19
|
+
| Prop | Type | Default | Required | Description |
|
|
20
|
+
|------|------|---------|----------|-------------|
|
|
21
|
+
| `children` | `ReactElement<OptionProps> \| ReactElement<OptionProps>[]` | - | Yes | Options to render in the dropdown |
|
|
22
|
+
| `placeholder` | `string` | - | No | Placeholder text when no option is selected |
|
|
23
|
+
| `leadingIcon` | `ReactNode` | - | No | Icon displayed before the select input |
|
|
24
|
+
| `expandIcon` | `ReactNode` | - | No | Custom expand/collapse icon |
|
|
25
|
+
| `selectedView` | `React.FC<any>` | - | No | Component to render selected items |
|
|
26
|
+
| `popoverOffset` | `[number, number]` | - | No | Offset for the dropdown popover position |
|
|
27
|
+
| `menuProps` | `ContentProps` | - | No | Additional props for the dropdown menu |
|
|
28
|
+
| `multiple` | `boolean` | `false` | No | Whether multiple selection is allowed |
|
|
29
|
+
| `hasDefaultOption` | `boolean` | `false` | No | Whether to include a default empty option |
|
|
30
|
+
| `defaultOpen` | `boolean` | `false` | No | Whether the dropdown is open by default |
|
|
31
|
+
| `open` | `boolean` | `false` | No | Controls the open state in controlled mode |
|
|
32
|
+
| `state` | `'Default' \| 'Error'` | `'Default'` | No | Visual state of the select component |
|
|
33
|
+
|
|
34
|
+
Plus all `ControlledFormComponentProps` (initialValue, value, onValueChange, disabled, required, invalid, id).
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
### Basic Usage
|
|
39
|
+
```tsx
|
|
40
|
+
import { Select, Option } from '@delightui/components';
|
|
41
|
+
|
|
42
|
+
function BasicExample() {
|
|
43
|
+
return (
|
|
44
|
+
<Select placeholder="Choose an option">
|
|
45
|
+
<Option value="option1">Option 1</Option>
|
|
46
|
+
<Option value="option2">Option 2</Option>
|
|
47
|
+
<Option value="option3">Option 3</Option>
|
|
48
|
+
</Select>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Controlled Select
|
|
54
|
+
```tsx
|
|
55
|
+
function ControlledExample() {
|
|
56
|
+
const [selectedValue, setSelectedValue] = useState('');
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Select
|
|
60
|
+
value={selectedValue}
|
|
61
|
+
onValueChange={setSelectedValue}
|
|
62
|
+
placeholder="Select a country"
|
|
63
|
+
>
|
|
64
|
+
<Option value="us">United States</Option>
|
|
65
|
+
<Option value="uk">United Kingdom</Option>
|
|
66
|
+
<Option value="ca">Canada</Option>
|
|
67
|
+
<Option value="au">Australia</Option>
|
|
68
|
+
</Select>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Multiple Selection
|
|
74
|
+
```tsx
|
|
75
|
+
function MultipleSelectionExample() {
|
|
76
|
+
const [selectedValues, setSelectedValues] = useState([]);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Select
|
|
80
|
+
multiple
|
|
81
|
+
value={selectedValues}
|
|
82
|
+
onValueChange={setSelectedValues}
|
|
83
|
+
placeholder="Select multiple options"
|
|
84
|
+
>
|
|
85
|
+
<Option value="red">Red</Option>
|
|
86
|
+
<Option value="green">Green</Option>
|
|
87
|
+
<Option value="blue">Blue</Option>
|
|
88
|
+
<Option value="yellow">Yellow</Option>
|
|
89
|
+
<Option value="purple">Purple</Option>
|
|
90
|
+
</Select>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Select with Icons
|
|
96
|
+
```tsx
|
|
97
|
+
function IconSelectExample() {
|
|
98
|
+
return (
|
|
99
|
+
<div className="icon-selects">
|
|
100
|
+
<Select
|
|
101
|
+
leadingIcon={<Icon icon="Location" />}
|
|
102
|
+
placeholder="Select location"
|
|
103
|
+
>
|
|
104
|
+
<Option value="ny">New York</Option>
|
|
105
|
+
<Option value="la">Los Angeles</Option>
|
|
106
|
+
<Option value="sf">San Francisco</Option>
|
|
107
|
+
</Select>
|
|
108
|
+
|
|
109
|
+
<Select
|
|
110
|
+
leadingIcon={<Icon icon="Person" />}
|
|
111
|
+
expandIcon={<Icon icon="ExpandMore" />}
|
|
112
|
+
placeholder="Select user"
|
|
113
|
+
>
|
|
114
|
+
<Option value="john">John Doe</Option>
|
|
115
|
+
<Option value="jane">Jane Smith</Option>
|
|
116
|
+
<Option value="bob">Bob Johnson</Option>
|
|
117
|
+
</Select>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Form Integration
|
|
124
|
+
```tsx
|
|
125
|
+
function FormSelectExample() {
|
|
126
|
+
const handleSubmit = (values, setError) => {
|
|
127
|
+
if (!values.category) {
|
|
128
|
+
setError('category', 'Please select a category');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
console.log('Form submitted:', values);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<Form onSubmit={handleSubmit}>
|
|
136
|
+
<FormField name="title" label="Title" required>
|
|
137
|
+
<Input placeholder="Enter title" />
|
|
138
|
+
</FormField>
|
|
139
|
+
|
|
140
|
+
<FormField name="category" label="Category" required>
|
|
141
|
+
<Select placeholder="Choose a category">
|
|
142
|
+
<Option value="tech">Technology</Option>
|
|
143
|
+
<Option value="business">Business</Option>
|
|
144
|
+
<Option value="design">Design</Option>
|
|
145
|
+
<Option value="marketing">Marketing</Option>
|
|
146
|
+
</Select>
|
|
147
|
+
</FormField>
|
|
148
|
+
|
|
149
|
+
<FormField name="tags" label="Tags">
|
|
150
|
+
<Select multiple placeholder="Select tags">
|
|
151
|
+
<Option value="urgent">Urgent</Option>
|
|
152
|
+
<Option value="important">Important</Option>
|
|
153
|
+
<Option value="draft">Draft</Option>
|
|
154
|
+
<Option value="review">Needs Review</Option>
|
|
155
|
+
</Select>
|
|
156
|
+
</FormField>
|
|
157
|
+
|
|
158
|
+
<Button actionType="submit">Submit</Button>
|
|
159
|
+
</Form>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Select with Validation
|
|
165
|
+
```tsx
|
|
166
|
+
function ValidationSelectExample() {
|
|
167
|
+
const [selected, setSelected] = useState('');
|
|
168
|
+
const [error, setError] = useState('');
|
|
169
|
+
|
|
170
|
+
const handleChange = (value) => {
|
|
171
|
+
setSelected(value);
|
|
172
|
+
if (value) {
|
|
173
|
+
setError('');
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const validateSelection = () => {
|
|
178
|
+
if (!selected) {
|
|
179
|
+
setError('Please make a selection');
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<div className="validation-select">
|
|
187
|
+
<Select
|
|
188
|
+
value={selected}
|
|
189
|
+
onValueChange={handleChange}
|
|
190
|
+
placeholder="Select an option"
|
|
191
|
+
state={error ? 'Error' : 'Default'}
|
|
192
|
+
>
|
|
193
|
+
<Option value="option1">Option 1</Option>
|
|
194
|
+
<Option value="option2">Option 2</Option>
|
|
195
|
+
<Option value="option3">Option 3</Option>
|
|
196
|
+
</Select>
|
|
197
|
+
|
|
198
|
+
{error && (
|
|
199
|
+
<Text type="BodySmall" className="error-message">
|
|
200
|
+
{error}
|
|
201
|
+
</Text>
|
|
202
|
+
)}
|
|
203
|
+
|
|
204
|
+
<Button onClick={validateSelection}>
|
|
205
|
+
Validate Selection
|
|
206
|
+
</Button>
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Disabled Select
|
|
213
|
+
```tsx
|
|
214
|
+
function DisabledSelectExample() {
|
|
215
|
+
return (
|
|
216
|
+
<div className="disabled-selects">
|
|
217
|
+
<Select disabled placeholder="Disabled select">
|
|
218
|
+
<Option value="option1">Option 1</Option>
|
|
219
|
+
<Option value="option2">Option 2</Option>
|
|
220
|
+
</Select>
|
|
221
|
+
|
|
222
|
+
<Select
|
|
223
|
+
disabled
|
|
224
|
+
value="option1"
|
|
225
|
+
placeholder="Disabled with value"
|
|
226
|
+
>
|
|
227
|
+
<Option value="option1">Selected Option</Option>
|
|
228
|
+
<Option value="option2">Option 2</Option>
|
|
229
|
+
</Select>
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Select with Groups
|
|
236
|
+
```tsx
|
|
237
|
+
function GroupedSelectExample() {
|
|
238
|
+
return (
|
|
239
|
+
<Select placeholder="Select a framework">
|
|
240
|
+
{/* Frontend Options */}
|
|
241
|
+
<Option disabled>Frontend</Option>
|
|
242
|
+
<Option value="react">React</Option>
|
|
243
|
+
<Option value="vue">Vue.js</Option>
|
|
244
|
+
<Option value="angular">Angular</Option>
|
|
245
|
+
|
|
246
|
+
{/* Backend Options */}
|
|
247
|
+
<Option disabled>Backend</Option>
|
|
248
|
+
<Option value="node">Node.js</Option>
|
|
249
|
+
<Option value="python">Python</Option>
|
|
250
|
+
<Option value="ruby">Ruby</Option>
|
|
251
|
+
|
|
252
|
+
{/* Database Options */}
|
|
253
|
+
<Option disabled>Database</Option>
|
|
254
|
+
<Option value="mongodb">MongoDB</Option>
|
|
255
|
+
<Option value="postgresql">PostgreSQL</Option>
|
|
256
|
+
<Option value="mysql">MySQL</Option>
|
|
257
|
+
</Select>
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Searchable Select
|
|
263
|
+
```tsx
|
|
264
|
+
function SearchableSelectExample() {
|
|
265
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
266
|
+
const options = [
|
|
267
|
+
{ value: 'apple', label: 'Apple' },
|
|
268
|
+
{ value: 'banana', label: 'Banana' },
|
|
269
|
+
{ value: 'cherry', label: 'Cherry' },
|
|
270
|
+
{ value: 'date', label: 'Date' },
|
|
271
|
+
{ value: 'elderberry', label: 'Elderberry' }
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
const filteredOptions = options.filter(option =>
|
|
275
|
+
option.label.toLowerCase().includes(searchTerm.toLowerCase())
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
const OptionComponent = ({ value, label }) => (
|
|
279
|
+
<Option value={value}>
|
|
280
|
+
{label}
|
|
281
|
+
</Option>
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<div className="searchable-select">
|
|
286
|
+
<Input
|
|
287
|
+
placeholder="Search options..."
|
|
288
|
+
value={searchTerm}
|
|
289
|
+
onValueChange={setSearchTerm}
|
|
290
|
+
leadingIcon={<Icon icon="Search" />}
|
|
291
|
+
/>
|
|
292
|
+
|
|
293
|
+
<Select placeholder="Select a fruit">
|
|
294
|
+
<List
|
|
295
|
+
data={filteredOptions}
|
|
296
|
+
component={OptionComponent}
|
|
297
|
+
keyExtractor={(option) => option.value}
|
|
298
|
+
/>
|
|
299
|
+
</Select>
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Custom Selected View
|
|
306
|
+
```tsx
|
|
307
|
+
function CustomSelectedViewExample() {
|
|
308
|
+
const CustomSelectedView = ({ selectedItems }) => {
|
|
309
|
+
const ChipComponent = ({ value, label }) => (
|
|
310
|
+
<Chip size="Small">
|
|
311
|
+
{label}
|
|
312
|
+
</Chip>
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<div className="custom-selected">
|
|
317
|
+
{selectedItems.length > 0 ? (
|
|
318
|
+
<div className="selected-items">
|
|
319
|
+
<List
|
|
320
|
+
data={selectedItems.slice(0, 2)}
|
|
321
|
+
component={ChipComponent}
|
|
322
|
+
keyExtractor={(item) => item.value}
|
|
323
|
+
/>
|
|
324
|
+
{selectedItems.length > 2 && (
|
|
325
|
+
<Text type="BodySmall">
|
|
326
|
+
+{selectedItems.length - 2} more
|
|
327
|
+
</Text>
|
|
328
|
+
)}
|
|
329
|
+
</div>
|
|
330
|
+
) : (
|
|
331
|
+
<Text type="BodyMedium" className="placeholder">
|
|
332
|
+
Select items...
|
|
333
|
+
</Text>
|
|
334
|
+
)}
|
|
335
|
+
</div>
|
|
336
|
+
);
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
<Select
|
|
341
|
+
multiple
|
|
342
|
+
selectedView={CustomSelectedView}
|
|
343
|
+
placeholder="Select multiple items"
|
|
344
|
+
>
|
|
345
|
+
<Option value="item1">Item 1</Option>
|
|
346
|
+
<Option value="item2">Item 2</Option>
|
|
347
|
+
<Option value="item3">Item 3</Option>
|
|
348
|
+
<Option value="item4">Item 4</Option>
|
|
349
|
+
<Option value="item5">Item 5</Option>
|
|
350
|
+
</Select>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Dynamic Options
|
|
356
|
+
```tsx
|
|
357
|
+
function DynamicOptionsExample() {
|
|
358
|
+
const [category, setCategory] = useState('');
|
|
359
|
+
const [subcategory, setSubcategory] = useState('');
|
|
360
|
+
|
|
361
|
+
const subcategories = {
|
|
362
|
+
tech: ['Frontend', 'Backend', 'Mobile', 'DevOps'],
|
|
363
|
+
business: ['Marketing', 'Sales', 'Operations', 'Strategy'],
|
|
364
|
+
design: ['UI/UX', 'Graphic', 'Product', 'Branding']
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
<div className="dynamic-selects">
|
|
369
|
+
<Select
|
|
370
|
+
value={category}
|
|
371
|
+
onValueChange={(value) => {
|
|
372
|
+
setCategory(value);
|
|
373
|
+
setSubcategory(''); // Reset subcategory when category changes
|
|
374
|
+
}}
|
|
375
|
+
placeholder="Select category"
|
|
376
|
+
>
|
|
377
|
+
<Option value="tech">Technology</Option>
|
|
378
|
+
<Option value="business">Business</Option>
|
|
379
|
+
<Option value="design">Design</Option>
|
|
380
|
+
</Select>
|
|
381
|
+
|
|
382
|
+
{category && (
|
|
383
|
+
<Select
|
|
384
|
+
value={subcategory}
|
|
385
|
+
onValueChange={setSubcategory}
|
|
386
|
+
placeholder="Select subcategory"
|
|
387
|
+
>
|
|
388
|
+
<List
|
|
389
|
+
data={subcategories[category].map(sub => ({ label: sub, value: sub.toLowerCase() }))}
|
|
390
|
+
component={({ label, value }) => (
|
|
391
|
+
<Option value={value}>
|
|
392
|
+
{label}
|
|
393
|
+
</Option>
|
|
394
|
+
)}
|
|
395
|
+
keyExtractor={(item) => item.value}
|
|
396
|
+
/>
|
|
397
|
+
</Select>
|
|
398
|
+
)}
|
|
399
|
+
</div>
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
```
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# SortableTrigger
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
A trigger component that initiates drag-and-drop functionality for sortable list items. It provides a dedicated draggable handle with proper visual feedback and accessibility features, allowing users to easily identify and interact with sortable elements.
|
|
6
|
+
|
|
7
|
+
## Aliases
|
|
8
|
+
|
|
9
|
+
- SortableTrigger
|
|
10
|
+
- DragHandle
|
|
11
|
+
- DragTrigger
|
|
12
|
+
- SortHandle
|
|
13
|
+
|
|
14
|
+
## Props Breakdown
|
|
15
|
+
|
|
16
|
+
**Extends:** Standard HTML button attributes
|
|
17
|
+
|
|
18
|
+
| Prop | Type | Default | Required | Description |
|
|
19
|
+
|------|------|---------|----------|-------------|
|
|
20
|
+
| `children` | `ReactNode` | - | No | Content to display within the trigger (usually an icon) |
|
|
21
|
+
| `className` | `string` | - | No | Additional CSS class names |
|
|
22
|
+
| `disabled` | `boolean` | `false` | No | Whether the drag trigger is disabled |
|
|
23
|
+
| `onDragStart` | `(event: DragEvent) => void` | - | No | Callback fired when drag starts |
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
### Basic Drag Handle
|
|
28
|
+
```tsx
|
|
29
|
+
import { SortableTrigger, Icon } from '@delightui/components';
|
|
30
|
+
|
|
31
|
+
function BasicTriggerExample() {
|
|
32
|
+
return (
|
|
33
|
+
<div className="sortable-item">
|
|
34
|
+
<SortableTrigger>
|
|
35
|
+
<Icon icon="DragHandle" size="Small" />
|
|
36
|
+
</SortableTrigger>
|
|
37
|
+
<span>Draggable content</span>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Custom Styled Trigger
|
|
44
|
+
```tsx
|
|
45
|
+
function CustomStyledTriggerExample() {
|
|
46
|
+
return (
|
|
47
|
+
<div className="task-item">
|
|
48
|
+
<SortableTrigger className="custom-drag-handle">
|
|
49
|
+
⋮⋮
|
|
50
|
+
</SortableTrigger>
|
|
51
|
+
|
|
52
|
+
<div className="task-content">
|
|
53
|
+
<h4>Task Title</h4>
|
|
54
|
+
<p>Task description</p>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Conditional Trigger
|
|
62
|
+
```tsx
|
|
63
|
+
function ConditionalTriggerExample() {
|
|
64
|
+
const [canReorder, setCanReorder] = useState(true);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div className="item-with-conditional-trigger">
|
|
68
|
+
<SortableTrigger disabled={!canReorder}>
|
|
69
|
+
<Icon icon="DragHandle" size="Small" />
|
|
70
|
+
</SortableTrigger>
|
|
71
|
+
|
|
72
|
+
<div className="item-content">
|
|
73
|
+
Content that can be reordered
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<button onClick={() => setCanReorder(!canReorder)}>
|
|
77
|
+
{canReorder ? 'Disable' : 'Enable'} Reordering
|
|
78
|
+
</button>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|