cleanplate 0.2.9 → 0.3.1

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.
@@ -14,7 +14,7 @@ FormControls is a set of form primitives exported as a namespace: `FormControls.
14
14
  | Radio | Radio group (array-based) | name, label, options, value, defaultValue, onChange(value, e), orientation, variant |
15
15
  | File | File picker with `button` / `card` variants, drag-and-drop, and a removable file list | name, label, variant, multiple, accept, value (File[]), onChange(files, e), buttonLabel, dropZoneText |
16
16
  | Toggle | On/off switch | checked, defaultChecked, onChange(checked: boolean) |
17
- | Stepper | Text input for step flows | placeholder, value, onChange(e) |
17
+ | Stepper | Numeric value with integrated − / + (integer text field + `min` / `max` / `step`) | placeholder, value, onChange(e), min, max, step, layout |
18
18
 
19
19
  ## Types
20
20
 
@@ -137,7 +137,7 @@ interface InputProps {
137
137
  - **CheckboxProps**: `options` (non-empty `CheckboxOption[]`), `name`, `label` (group `<legend>`), optional `id`, `value` (`CheckboxValue[]`), `defaultValue` (`CheckboxValue[]`), `onChange(values, e)`, `orientation` (`"vertical" | "horizontal"`), `variant` (`"default" | "card"`), `isDisabled`, `isRequired`, `isFluid`, `className`, `error`, `dataTestId`.
138
138
  - **CheckboxOption**: `{ label, value, isDisabled?, description?, icon?, dataTestId?, id? }`. `description` is rendered under the option label as muted secondary text and linked via `aria-describedby`. `icon` accepts any `ReactNode` (e.g. `<Icon />`, `<img />`, custom SVG) and renders to the left of the label/description. `CheckboxValue = string | number`.
139
139
  - **DateProps**: `value` / `defaultValue` (`Date | null`), `onChange(date: Date | null)`, `placeholder`, **`dateFormat`** (display string via `date-fns` + `locale`, default `MMM dd, yyyy`), **`name`** (renders a hidden `<input>` that submits **`yyyy-MM-dd`** for the committed calendar date), **`minDate`** / **`maxDate`** (inclusive navigation + selection bounds), **`disabledDates`** / **`disabledDaysOfWeek`** (greyed cells), **`locale`** (`date-fns` `Locale` — grid, subview copy, and field text), **`weekStartsOn`** (`0`–`6`, default `0` = Sunday), **`clearable`** (default `true`; shows clear control when a value exists), **`readOnly`** (no picker; value fixed), **`popoverPlacement`** (Floating UI placement for desktop; default `bottom-start`), **`onOpen`** / **`onClose`**, plus shared `label`, `isDisabled`, `isRequired`, `isFluid`, `className`, `error`, `dataTestId`.
140
- - **FormControlsStepperProps**: label, placeholder, value/defaultValue, onChange(e), type, isDisabled, isRequired, isFluid, className, error, dataTestId.
140
+ - **FormControlsStepperProps**: label, placeholder, value/defaultValue, onChange(e), min, max, step, layout (`"default" | "split-controls" | "trailing-stacked-chevrons"`), isDisabled, isRequired, isFluid, className, error, dataTestId.
141
141
 
142
142
  ## Usage Examples
143
143
 
@@ -1,31 +1,31 @@
1
1
  # MediaObject Component
2
2
 
3
- Purpose: A flexible layout pattern that combines media (icon, image, or avatar) with content (title and description). Perfect for displaying user profiles, product cards, notifications, and any content that needs a media element alongside text.
3
+ Purpose: Combines fixed media (`Avatar`: icon, image, or initials) with a dense text stack (primary title, optional subtitle, optional clipped preview). Supports an optional **trailing rail** for metadata aligned to the title row (e.g. date) and an action anchored to the last text row (e.g. star), matching mobile inbox/list cards. Built-in text colors use semantic tokens (`--text-default`, `--text-subtle`, `--text-muted`).
4
4
 
5
5
  ## Props / Inputs
6
6
 
7
7
  | Prop | Type | Required | Default | Description |
8
8
  | --- | --- | --- | --- | --- |
9
- | title | string | yes | — | The main title text displayed in the content area. |
10
- | mediaIcon | string | no | "" | Icon name from Material Symbols to display as the media element. |
11
- | mediaImage | string | no | "" | Image URL to display as the media element. |
12
- | mediaAvatar | string | no | "" | Name used to generate an avatar with initials and background color. |
13
- | description | string | no | — | Secondary text displayed below the title. |
14
- | margin | string \| string[] | no | "0" | Spacing utility token(s) for outer margin, such as `m-0` or `["m-1", "m-b-2"]`. |
15
- | padding | string \| string[] | no | "0" | Spacing utility token(s) for inner padding, such as `p-0` or `["p-1", "p-b-2"]`. |
16
- | className | string | no | "media-object" | Additional class names for the root element. |
17
- | onClick | function | no | — | Called with the click event when the media object is clicked. |
18
- | ...rest | React.HTMLAttributes<HTMLDivElement> | no | | All other standard HTML div attributes are supported and passed through to the rendered element. |
9
+ | title | string | yes | — | Primary line (e.g. name, sender). Rendered emphasized. |
10
+ | mediaIcon | `MaterialIconName` \| string | no | "" | Material Symbol name passed to `Avatar`. |
11
+ | mediaImage | string | no | "" | Image URL for `Avatar`. |
12
+ | mediaAvatar | string | no | "" | Display name used for initials and avatar color generation (when image/icon not shown). |
13
+ | subtitle | `React.ReactNode` | no | — | Optional middle line (e.g. subject). Omit for two-line layouts. |
14
+ | description | `React.ReactNode` | no | | Optional preview/snippet line(s); muted, multi-line ellipsis via `--cp-media-object-desc-lines`. |
15
+ | descriptionLineClamp | number | no | 2 | Max lines for `description` before truncation. |
16
+ | meta | `React.ReactNode` | no | | Trailing rail, **first text row**. Strings/numbers get subdued meta typography; pass JSX for custom styling. |
17
+ | action | `React.ReactNode` | no | — | Trailing rail, **last content row** (e.g. `Icon`/button); right-aligned with the snippet row when present. |
18
+ | margin | string \| `SpacingOption[]` | no | "0" | Outer margin spacing tokens (`m-*` utilities). |
19
+ | padding | string \| `SpacingOption[]` | no | "0" | Inner padding spacing tokens (`p-*` utilities). |
20
+ | className | string | no | "media-object" | Extra classes on the root `<div>`. |
21
+ | onClick | function | no | — | Click handler on the root; forwarded to `Avatar` when provided. |
22
+ | ...rest | `React.HTMLAttributes<HTMLDivElement>` | no | — | Standard div props (`id`, `data-*`, `aria-*`, etc.). |
19
23
 
20
24
  ## Types
21
25
 
22
- ### MediaObjectMargin
26
+ ### MediaObjectMargin / MediaObjectPadding
23
27
  ```typescript
24
28
  type MediaObjectMargin = string | SpacingOption[];
25
- ```
26
-
27
- ### MediaObjectPadding
28
- ```typescript
29
29
  type MediaObjectPadding = string | SpacingOption[];
30
30
  ```
31
31
 
@@ -37,11 +37,15 @@ type SpacingOption = typeof SPACING_OPTIONS[number];
37
37
  ### MediaObjectProps
38
38
  ```typescript
39
39
  interface MediaObjectProps extends React.HTMLAttributes<HTMLDivElement> {
40
- mediaIcon?: string;
40
+ mediaIcon?: MaterialIconName | string;
41
41
  mediaImage?: string;
42
42
  mediaAvatar?: string;
43
43
  title: string;
44
- description?: string;
44
+ subtitle?: React.ReactNode;
45
+ description?: React.ReactNode;
46
+ descriptionLineClamp?: number;
47
+ meta?: React.ReactNode;
48
+ action?: React.ReactNode;
45
49
  margin?: MediaObjectMargin;
46
50
  padding?: MediaObjectPadding;
47
51
  className?: string;
@@ -51,253 +55,145 @@ interface MediaObjectProps extends React.HTMLAttributes<HTMLDivElement> {
51
55
 
52
56
  ## Usage Examples
53
57
 
54
- ### With icon
55
-
56
- ```jsx
57
- import { MediaObject } from "cleanplate";
58
-
59
- export const Example = () => (
60
- <MediaObject
61
- mediaIcon="person"
62
- title="User Profile"
63
- description="Manage your account settings and preferences"
64
- />
65
- );
66
- ```
67
-
68
- ### With image
58
+ ### Title + description (classic two-line row)
69
59
 
70
60
  ```jsx
71
61
  import { MediaObject } from "cleanplate";
72
62
 
73
- export const Example = () => (
74
- <MediaObject
75
- mediaImage="https://example.com/avatar.jpg"
76
- title="John Doe"
77
- description="Senior Developer at Tech Corp"
78
- />
79
- );
63
+ <MediaObject
64
+ mediaIcon="person"
65
+ title="User Profile"
66
+ description="Manage your account settings and preferences"
67
+ />
80
68
  ```
81
69
 
82
- ### With avatar (initials)
83
-
84
- ```jsx
85
- import { MediaObject } from "cleanplate";
86
-
87
- export const Example = () => (
88
- <MediaObject
89
- mediaAvatar="John Doe"
90
- title="John Doe"
91
- description="Senior Developer with 5+ years of experience"
92
- />
93
- );
94
- ```
70
+ ### Inbox-style row (three lines + date + star)
95
71
 
96
- ### Title only
72
+ Prefer `subtitle` + `description` plus `meta` / `action` for mail-style listings. Strings in `meta` use muted sizing; arbitrary JSX is allowed for custom summaries.
97
73
 
98
74
  ```jsx
99
- import { MediaObject } from "cleanplate";
100
-
101
- export const Example = () => (
102
- <MediaObject
103
- mediaIcon="settings"
104
- title="Settings"
105
- />
106
- );
75
+ import { Button, Icon, MediaObject } from "cleanplate";
76
+
77
+ <MediaObject
78
+ mediaAvatar="Ada Lovelace"
79
+ title="Ada Lovelace"
80
+ subtitle="» Weekly digest — infra"
81
+ description="Deployments, deprecation notices, and the FAQ refresh you requested."
82
+ meta="Thu"
83
+ descriptionLineClamp={2}
84
+ action={
85
+ <Button type="button" variant="icon" aria-label="Star thread" margin="m-0">
86
+ <Icon name="star_border" />
87
+ </Button>
88
+ }
89
+ />
107
90
  ```
108
91
 
109
- ### With margin and padding
92
+ ### Subtitle without snippet
110
93
 
111
94
  ```jsx
112
- import { MediaObject } from "cleanplate";
113
-
114
- export const Example = () => (
115
- <>
116
- <MediaObject
117
- mediaIcon="star"
118
- title="Featured Item"
119
- description="This item has custom spacing"
120
- margin="m-3"
121
- padding="p-2"
122
- />
123
- <MediaObject
124
- mediaIcon="heart"
125
- title="Another Item"
126
- description="With multiple margin tokens"
127
- margin={["m-1", "m-b-3"]}
128
- padding={["p-1", "p-x-2"]}
129
- />
130
- </>
131
- );
95
+ <MediaObject
96
+ mediaIcon="payments"
97
+ title="Invoice #4021"
98
+ subtitle="Reminder: payable on receipt."
99
+ meta="Unpaid"
100
+ />
132
101
  ```
133
102
 
134
- ### Clickable media object
103
+ ### Action only on the trailing rail
135
104
 
136
105
  ```jsx
137
- import { MediaObject } from "cleanplate";
138
-
139
- export const Example = () => (
140
- <MediaObject
141
- mediaImage="https://example.com/product.jpg"
142
- title="Wireless Headphones"
143
- description="High-quality wireless headphones"
144
- onClick={() => console.log("Media object clicked")}
145
- />
146
- );
106
+ <MediaObject
107
+ mediaIcon="shopping_bag"
108
+ title="Reorder suggestions"
109
+ description="Based on your last three carts."
110
+ action={<Icon name="more_vert" />}
111
+ />
147
112
  ```
148
113
 
149
- ### User profile card
114
+ ### Meta as custom JSX
150
115
 
151
116
  ```jsx
152
- import { MediaObject } from "cleanplate";
153
- import { Button } from "cleanplate";
154
-
155
- export const Example = () => (
156
- <div style={{
157
- border: "1px solid #e0e0e0",
158
- borderRadius: "8px",
159
- padding: "16px"
160
- }}>
161
- <MediaObject
162
- mediaImage="https://example.com/avatar.jpg"
163
- title="John Doe"
164
- description="Senior Developer • john.doe@example.com"
165
- />
166
- <Button variant="outline" size="small" margin="m-t-3">
167
- Edit Profile
168
- </Button>
169
- </div>
170
- );
117
+ import { MediaObject, Typography } from "cleanplate";
118
+
119
+ <MediaObject
120
+ mediaAvatar="Jane"
121
+ title="Jane Doe"
122
+ description="Ping when you merge."
123
+ meta={
124
+ <Typography variant="small" margin="m-0" isBold align="right">
125
+ 3 new
126
+ </Typography>
127
+ }
128
+ />
171
129
  ```
172
130
 
173
- ### Product card
131
+ ### With image or avatar initials
174
132
 
175
133
  ```jsx
176
- import { MediaObject } from "cleanplate";
177
- import { Typography } from "cleanplate";
178
- import { Button } from "cleanplate";
179
-
180
- export const Example = () => (
181
- <div style={{
182
- border: "1px solid #e0e0e0",
183
- borderRadius: "8px",
184
- padding: "16px"
185
- }}>
186
- <MediaObject
187
- mediaImage="https://example.com/product.jpg"
188
- title="Wireless Headphones"
189
- description="High-quality wireless headphones with noise cancellation"
190
- />
191
- <div style={{ marginTop: "12px", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
192
- <Typography variant="h5">$199.99</Typography>
193
- <Button size="small" variant="outline">Add to Cart</Button>
194
- </div>
195
- </div>
196
- );
134
+ <MediaObject
135
+ mediaImage="https://example.com/avatar.jpg"
136
+ title="John Doe"
137
+ description="Senior Developer at Tech Corp"
138
+ />
139
+
140
+ <MediaObject
141
+ mediaAvatar="John Doe"
142
+ title="John Doe"
143
+ description="Shows initials until an image overrides."
144
+ />
197
145
  ```
198
146
 
199
- ### Notification list
147
+ ### Title only
200
148
 
201
149
  ```jsx
202
- import { MediaObject } from "cleanplate";
203
-
204
- export const Example = () => (
205
- <div>
206
- <MediaObject
207
- mediaIcon="notifications"
208
- title="New Message"
209
- description="You have received a new message from Jane Smith"
210
- margin="m-b-2"
211
- />
212
- <MediaObject
213
- mediaIcon="schedule"
214
- title="Meeting Reminder"
215
- description="Team standup in 30 minutes"
216
- margin="m-b-2"
217
- />
218
- <MediaObject
219
- mediaIcon="done"
220
- title="Task Completed"
221
- description="Your project 'Website Redesign' has been updated"
222
- />
223
- </div>
224
- );
150
+ <MediaObject mediaIcon="settings" title="Settings" />
225
151
  ```
226
152
 
227
- ### Settings items
153
+ ### Margin / padding tokens
228
154
 
229
155
  ```jsx
230
- import { MediaObject } from "cleanplate";
231
-
232
- export const Example = () => (
233
- <div>
234
- <MediaObject
235
- mediaIcon="settings"
236
- title="Account Settings"
237
- description="Manage your account preferences and security settings"
238
- margin="m-b-2"
239
- />
240
- <MediaObject
241
- mediaIcon="lock"
242
- title="Privacy & Security"
243
- description="Control your privacy settings and security options"
244
- margin="m-b-2"
245
- />
246
- <MediaObject
247
- mediaIcon="notifications"
248
- title="Notifications"
249
- description="Configure how and when you receive notifications"
250
- />
251
- </div>
252
- );
156
+ <MediaObject
157
+ mediaIcon="star"
158
+ title="Featured"
159
+ description="With spacing utilities"
160
+ margin="m-b-3"
161
+ padding="p-2"
162
+ />
163
+ <MediaObject
164
+ margin={["m-1", "m-b-3"]}
165
+ padding={["p-1", "p-x-2"]}
166
+ title="Row"
167
+ />
253
168
  ```
254
169
 
255
- ### List of users
170
+ ### Clickable row
256
171
 
257
172
  ```jsx
258
- import { MediaObject } from "cleanplate";
259
-
260
- export const Example = () => {
261
- const users = [
262
- { name: "John Doe", role: "Developer", avatar: "https://example.com/avatar1.jpg" },
263
- { name: "Jane Smith", role: "Designer", avatar: "https://example.com/avatar2.jpg" },
264
- { name: "Mike Johnson", role: "Manager", avatar: "https://example.com/avatar3.jpg" }
265
- ];
266
-
267
- return (
268
- <div>
269
- {users.map((user, index) => (
270
- <MediaObject
271
- key={index}
272
- mediaImage={user.avatar}
273
- title={user.name}
274
- description={user.role}
275
- margin={index < users.length - 1 ? "m-b-2" : "m-0"}
276
- />
277
- ))}
278
- </div>
279
- );
280
- };
173
+ <MediaObject
174
+ mediaImage="/product.jpg"
175
+ title="Wireless Headphones"
176
+ description="Noise cancelling"
177
+ onClick={() => {}}
178
+ />
281
179
  ```
282
180
 
283
181
  ## Behavior Notes
284
182
 
285
- - The `title` prop is required and will always be displayed in bold text.
286
- - The `description` prop is optional. If not provided, only the title will be displayed.
287
- - You can provide one of `mediaIcon`, `mediaImage`, or `mediaAvatar` to display the media element. If multiple are provided, the component will prioritize in this order: `mediaAvatar` > `mediaImage` > `mediaIcon`.
288
- - The component uses the `Avatar` component internally to render the media element, which supports icons, images, and generated avatars with initials.
289
- - The component uses the `Typography` component internally for the title and description text.
290
- - The title is rendered with `isBold={true}` by default.
291
- - The description is rendered with `variant="small"`.
292
- - The component renders as a `<div>` element with a flex layout structure.
293
- - Margin and padding spacing accept either a single string token (e.g., `"m-2"`) or an array of tokens (e.g., `["m-1", "m-b-3"]`).
294
- - The `onClick` handler makes the entire media object clickable, useful for interactive lists or cards.
295
- - All standard HTML div attributes can be passed through, allowing for custom `id`, `data-*`, `aria-*`, and other attributes.
296
- - The component is designed to be responsive and adapts to different screen sizes.
183
+ - **Layout**: Root uses flex (media column + responsive grid body). Without `meta` or `action`, the body is one column of stacked text with **zero row-gap** between lines for inbox-like density.
184
+ - **Trailing rail**: If `meta` and/or `action` is passed, the body adds a second grid column: `meta` always occupies **column 2, row 1** aligned with `title`; `action` occupies **column 2, last text row**, aligned toward the snippet row. When **only one** logical text row remains and **both** `meta` and `action` exist, both stack in one compact trailing cell (`gap` 2px).
185
+ - **`descriptionLineClamp`** sets the CSS custom property `--cp-media-object-desc-lines` on the snippet wrapper so consumers can clamp 1–N lines preview text.
186
+ - **Media slot**: Implemented with `Avatar` (`name={mediaAvatar}`, `image`, `icon`). Combination rules when multiple props are set match `Avatar` (see `docs/Avatar.md`); typical usage passes one dominant source (photo URL vs icon vs name for initials).
187
+ - **`meta` primitives**: Strings and numbers render with component meta typography (muted `--text-muted`); pass React nodes when you control color/weight entirely.
188
+ - **`title`** is always a string rendered as emphasized primary text (`--text-default`).
189
+ - **`subtitle`** and **`description`** accept `React.ReactNode`; empty string / falsy hides the slot.
190
+ - **Styling**: Text stacks use semantic tokens (`--text-default`, `--text-subtle`, `--text-muted`); rules live in CSS modules—not the `Typography` component—so inbox-style line-heights stay consistent.
191
+ - **Spacing**: Margin and padding use the same spacing token shape as elsewhere (`string` or `SpacingOption[]` arrays).
192
+ - **Interaction**: Whole row is clickable when `onClick` is supplied; avatar receives the same handler for consistency.
297
193
 
298
194
  ## Related Components / Links
299
195
 
300
- - Avatar (used internally for the media element)
301
- - Typography (used internally for title and description)
302
- - Button (often used alongside MediaObject in cards)
303
- - Container (commonly used to wrap MediaObject components)
196
+ - **Avatar** media slot (`mediaAvatar`, `mediaImage`, `mediaIcon`).
197
+ - **Icon**, **Button** common `action` content.
198
+ - **Table** `mobileColumns` maps rows into `MediaObject` on narrow viewports (`title`, `description`, optional `mediaAvatar`; still compatible without `subtitle` / rail).
199
+ - **Container** often wraps stacked `MediaObject` rows.
package/docs/Stepper.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Purpose: Displays a sequence of steps (e.g. for a wizard or checkout). Each step has a label, optional active/completed state, and can be clickable. Use it for multi-step flows and progress indication. Supports horizontal and vertical layout.
4
4
 
5
+ **Note:** For a **numeric quantity** field with integrated − / +, use **`FormControls.Stepper`** (see `docs/FormControls.md`). This file documents the **wizard / progress** Stepper only.
6
+
5
7
  ## Props / Inputs
6
8
 
7
9
  | Prop | Type | Required | Default | Description |
package/llms.txt CHANGED
@@ -65,10 +65,11 @@ All component documentation is located in the `docs/` folder. The following docu
65
65
 
66
66
  ### MediaObject Component
67
67
  - File: `docs/MediaObject.md`
68
- - Purpose: A flexible layout pattern that combines media (icon, image, or avatar) with content (title and description). Perfect for displaying user profiles, product cards, notifications, and any content that needs a media element alongside text.
69
- - Key Features: Media types (icon, image, avatar), title and description, margin and padding spacing, clickable support
70
- - Types: MediaObjectProps, MediaObjectMargin, MediaObjectPadding, SpacingOption
71
- - Related Components: Uses Avatar and Typography internally
68
+ - Purpose: Avatar + dense text stack (required `title`, optional `subtitle`, optional line-clamped `description`) plus optional trailing **rail**: `meta` (row 1, e.g. date) and/or `action` (last row, e.g. star). Fits profiles, notifications, inbox-style rows, Table mobile cards.
69
+ - Key Features: `mediaIcon`/`mediaImage`/`mediaAvatar` `Avatar`; `subtitle`; `meta`/`action`; `descriptionLineClamp` `--cp-media-object-desc-lines`; semantic text tokens (`--text-default`/`--text-subtle`/`--text-muted`); sparse grid row-gap for list density
70
+ - Props (see full table in doc): title, subtitle?, description?, descriptionLineClamp? (default 2), meta?, action?, margin, padding, onClick?, ...div HTMLAttributes
71
+ - Types: MediaObjectProps, MediaObjectMargin, MediaObjectPadding, SpacingOption; `mediaIcon` accepts MaterialIconName
72
+ - Related Components: Avatar (media); Icon/Button often in `action`; Table (mobile view); Typography not wired internally—text styles live in MediaObject.module.scss
72
73
 
73
74
  ### Avatar Component
74
75
  - File: `docs/Avatar.md`
@@ -110,6 +111,7 @@ All component documentation is located in the `docs/` folder. The following docu
110
111
  - Key Features: Variants (horizontal, vertical), config-driven steps (StepperStepConfig), optional onClick(step), margin spacing (suffix API), completed state shows done icon
111
112
  - Types: StepperProps, StepperStepConfig, StepperVariant, StepperMargin, SpacingOption
112
113
  - Related Components: Container, Typography, Icon (used internally)
114
+ - **Not** the numeric +/- field: for quantity-style inputs use **`FormControls.Stepper`** (see **FormControls** / `docs/FormControls.md`).
113
115
 
114
116
  ### BreadCrumb Component
115
117
  - File: `docs/BreadCrumb.md`
@@ -176,10 +178,10 @@ All component documentation is located in the `docs/` folder. The following docu
176
178
 
177
179
  ### FormControls
178
180
  - File: `docs/FormControls.md`
179
- - Purpose: Set of form primitives: Input, TextArea, Select, Date, Checkbox, Radio, File, Toggle, Stepper. Access via FormControls.Input, FormControls.Select, etc.
180
- - Key Features: Input (supports text/search/number + prefix/suffix; number maps to numeric text input and clamps via `min`/`max` on blur; search adds icon + clear button), TextArea, **Select** (Floating UI: portalled combobox on desktop with flip/shift positioning; **viewport ≤768px** uses a **bottom sheet** shell with `role="dialog"` / `aria-modal` when a label exists; **sync** `options` with in-panel **search filter**, or **async** via `options={null}` + `onSearch` (debounced); **groups** (`Option.group` sticky headers); **multi** chips with `triggerMaxItems` **+N** overflow, **Select all / Clear all**, **`maxSelect`** cap; optional **`onAddOption`**; combobox + listbox ARIA, **`aria-invalid`** on error, **`aria-controls`** on trigger/search only while open; `name` + hidden input for forms — multi posts comma-joined **`value`s**), **Date** (calendar **`Date | null`** + **Cancel/OK** staging; `date-fns` **Locale** + **`dateFormat`**; **Floating UI** desktop popover **~max 400px** wide (viewport-capped) with **`popoverPlacement`**; **≤768px** **bottom sheet** + backdrop/body lock like Select; **month/year subviews** with back + **“Select a month of {yyyy}”** / **“Select a year for {MMMM}”** titles; trigger **`calendar_month`** icon + optional clear; **`minDate`/`maxDate`/`disabledDates`/`disabledDaysOfWeek`**, **`weekStartsOn`**, **`readOnly`**, **`clearable`**, **`name`** → hidden **`yyyy-MM-dd`**, **`onOpen`/`onClose`**), Checkbox (group-first: options[], CheckboxValue[] for multi-select, onChange(values, e); each option supports `description` and `icon`; `variant="card"` for tile-style options), Radio (group-first: options[], single value, onChange(value, e); each option supports `description` and `icon`; `variant="card"` for tile-style options), File (`variant="button" | "card"`; card variant has dashed drop zone with drag-and-drop; `value: File[]` + `onChange(files, e)`; renders a removable file list with type-aware thumbnail, name, and size), Toggle (switch semantics via checkbox + role="switch"), Stepper; common props label, isRequired, isFluid, error
181
- - Types: InputProps, **Option** (preferred), **SelectOption** (deprecated alias of Option), **SelectValue**, SelectProps, TextAreaProps, CheckboxProps, CheckboxOption, CheckboxValue, FileProps, FileVariant, RadioProps, RadioOption, RadioValue, ToggleProps, DateProps, FormControlsStepperProps
182
- - Theming: Public CSS custom property `--cp-form-control-radius` (default `var(--radius-large)` = 12px) controls corner radius for Input, TextArea, Stepper, Select trigger + open dropdown corners, Date trigger + calendar panel shell, File (outline trigger, drop zone, in-card CTA span, file list rows), and Radio/Checkbox `variant="card"` tiles. Override on `:root` (or any wrapper / inline `style`) after importing `cleanplate/dist/index.css` to retheme just form fields. Prefer this over overriding the underlying `--radius-large` design token, which affects every "large radius" surface in the framework.
181
+ - Purpose: Set of form primitives: Input, TextArea, Select, Date, Checkbox, Radio, File, Toggle, **FormControls.Stepper** (numeric +/− only; wizard step UI is the separate **`Stepper`** export — `docs/Stepper.md`). Access via `FormControls.Input`, `FormControls.Select`, etc.
182
+ - Key Features: Input (supports text/search/number + prefix/suffix; number maps to numeric text input and clamps via `min`/`max` on blur; search adds icon + clear button), TextArea, **Select** (Floating UI: portalled combobox on desktop with flip/shift positioning; **viewport ≤768px** uses a **bottom sheet** shell with `role="dialog"` / `aria-modal` when a label exists; **sync** `options` with in-panel **search filter**, or **async** via `options={null}` + `onSearch` (debounced); **groups** (`Option.group` sticky headers); **multi** chips with `triggerMaxItems` **+N** overflow, **Select all / Clear all**, **`maxSelect`** cap; optional **`onAddOption`**; combobox + listbox ARIA, **`aria-invalid`** on error, **`aria-controls`** on trigger/search only while open; `name` + hidden input for forms — multi posts comma-joined **`value`s**), **Date** (calendar **`Date | null`** + **Cancel/OK** staging; `date-fns` **Locale** + **`dateFormat`**; **Floating UI** desktop popover **~max 400px** wide (viewport-capped) with **`popoverPlacement`**; **≤768px** **bottom sheet** + backdrop/body lock like Select; **month/year subviews** with back + **“Select a month of {yyyy}”** / **“Select a year for {MMMM}”** titles; trigger **`calendar_month`** icon + optional clear; **`minDate`/`maxDate`/`disabledDates`/`disabledDaysOfWeek`**, **`weekStartsOn`**, **`readOnly`**, **`clearable`**, **`name`** → hidden **`yyyy-MM-dd`**, **`onOpen`/`onClose`**), Checkbox (group-first: options[], CheckboxValue[] for multi-select, onChange(values, e); each option supports `description` and `icon`; `variant="card"` for tile-style options), Radio (group-first: options[], single value, onChange(value, e); each option supports `description` and `icon`; `variant="card"` for tile-style options), File (`variant="button" | "card"`; card variant has dashed drop zone with drag-and-drop; `value: File[]` + `onChange(files, e)`; renders a removable file list with type-aware thumbnail, name, and size), Toggle (switch semantics via checkbox + role="switch"), **FormControls.Stepper** (numeric integer only: inner field is `type="text"` + `inputMode="numeric"` with − / +; `min`, `max`, `step`; **`layout`**: `"default"` | `"split-controls"` | `"trailing-stacked-chevrons"`; no **`type`** prop — use **Input** for other field kinds; − / + clicks do not move focus into the field, avoiding unwanted mobile keyboard); common props label, isRequired, isFluid, error
183
+ - Types: InputProps, **Option** (preferred), **SelectOption** (deprecated alias of Option), **SelectValue**, SelectProps, TextAreaProps, CheckboxProps, CheckboxOption, CheckboxValue, FileProps, FileVariant, RadioProps, RadioOption, RadioValue, ToggleProps, DateProps, FormControlsStepperProps, **FormControlsStepperLayout**
184
+ - Theming: Public CSS custom property `--cp-form-control-radius` (default `var(--radius-large)` = 12px) controls corner radius for Input, TextArea, **FormControls.Stepper** shell, Select trigger + open dropdown corners, Date trigger + calendar panel shell, File (outline trigger, drop zone, in-card CTA span, file list rows), and Radio/Checkbox `variant="card"` tiles. Override on `:root` (or any wrapper / inline `style`) after importing `cleanplate/dist/index.css` to retheme just form fields. Prefer this over overriding the underlying `--radius-large` design token, which affects every "large radius" surface in the framework.
183
185
  - Related Components: Pills (Input), Pagination (Select), Container, Button
184
186
 
185
187
  ### Toast Component
@@ -254,7 +256,7 @@ All documentation files follow a consistent format:
254
256
  | Dropdown | `docs/Dropdown.md` | Menus and floating panels |
255
257
  | Alert | `docs/Alert.md` | Inline feedback and notices |
256
258
  | Spinner | `docs/Spinner.md` | Loading and activity indicator |
257
- | Stepper | `docs/Stepper.md` | Multi-step flows and wizards |
259
+ | Stepper | `docs/Stepper.md` | Wizard / checkout **step progress** (not numeric +/−; that is **FormControls.Stepper** in FormControls) |
258
260
  | BreadCrumb | `docs/BreadCrumb.md` | Navigation trail and page hierarchy |
259
261
  | Accordion | `docs/Accordion.md` | Collapsible panels and FAQ sections |
260
262
  | ConfirmDialog | `docs/ConfirmDialog.md` | Confirmation modals and destructive actions |
@@ -264,7 +266,7 @@ All documentation files follow a consistent format:
264
266
  | Badge | `docs/Badge.md` | Status labels, tags, and counts with colored variants |
265
267
  | Pills | `docs/Pills.md` | Tags/chips with read-only, edit, and remove modes |
266
268
  | Animated | `docs/Animated.md` | Scroll-triggered entrance/exit animations |
267
- | FormControls | `docs/FormControls.md` | Form primitives (Input, Select, TextArea, Date, Checkbox, etc.) |
269
+ | FormControls | `docs/FormControls.md` | Form primitives (Input, Select, TextArea, Date, Checkbox, **numeric Stepper**, etc.) |
268
270
  | Toast | `docs/Toast.md` | Transient messages via ref.addMessage |
269
271
  | MenuList | `docs/MenuList.md` | Nav menus and link lists with icons |
270
272
  | Header | `docs/Header.md` | Responsive navigation header with logo and menu |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cleanplate",
3
- "version": "0.2.9",
3
+ "version": "0.3.1",
4
4
  "description": "CleanPlate - A Headless React UI Framework",
5
5
  "files": [
6
6
  "dist",