cleanplate 0.3.29 → 0.3.30

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/docs/Drawer.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Drawer Component
2
2
 
3
- Purpose: A slide-in overlay panel for navigation, filters, settings, or detail views. On desktop (≥768px), slides from a configurable edge (`left`, `right`, `top`, or `bottom`). Below 768px, always renders as a bottom sheet (max height 90dvh). Includes fade/slide transitions, focus management, and Modal-like optional chrome (title, close button, footer actions).
3
+ Purpose: A slide-in overlay panel for navigation, filters, settings, or detail views. On desktop (≥768px), slides from a configurable edge (`left`, `right`, `top`, or `bottom`). Below 768px, always renders as a bottom sheet (max height 90dvh). Includes fade/slide transitions, focus management, and Modal-like optional chrome (title, close button, footer primary / secondary / tertiary actions).
4
4
 
5
5
  ## Props / Inputs
6
6
 
@@ -27,7 +27,20 @@ Purpose: A slide-in overlay panel for navigation, filters, settings, or detail v
27
27
  | onPrimaryButtonClick | () => void | no | — | Called when the primary footer button is clicked. |
28
28
  | secondaryButtonLabel | string | no | "" | Label for the secondary footer button; empty hides it. |
29
29
  | onSecondaryButtonClick | () => void | no | — | Called when the secondary footer button is clicked. |
30
- | dataTestId | string | no | | Root `data-testid` on the dialog panel; suffixed ids on overlay, header, title, close, body, footer, and action buttons (see **E2E / test selectors**). |
30
+ | tertiaryButtonLabel | string | no | "" | Label for the tertiary footer button (ghost variant); empty hides it. |
31
+ | onTertiaryButtonClick | () => void | no | — | Called when the tertiary footer button is clicked. |
32
+ | dataTestId | string | no | — | Root `data-testid` on the dialog panel; suffixed ids: `-overlay`, `-header`, `-title`, `-close`, `-body`, `-footer`, `-primary`, `-secondary`, `-tertiary` (see **E2E / test selectors**). |
33
+
34
+ ## Size presets
35
+
36
+ | Size | Side drawer width (`left` / `right`) | Top / bottom drawer height |
37
+ | --- | --- | --- |
38
+ | `small` | `min(280px, 100vw − 56px)` | `30dvh` |
39
+ | `medium` | `min(360px, 100vw − 56px)` | `50dvh` |
40
+ | `large` | `min(480px, 100vw − 56px)` | `70dvh` |
41
+ | `full` | `100vw` | `100dvh` |
42
+
43
+ On mobile (≤768px), `size` height/width presets are overridden by the bottom sheet shell (`max-height: 90dvh`, full width).
31
44
 
32
45
  ## Types
33
46
 
@@ -75,6 +88,8 @@ interface DrawerProps {
75
88
  onPrimaryButtonClick?: () => void;
76
89
  secondaryButtonLabel?: string;
77
90
  onSecondaryButtonClick?: () => void;
91
+ tertiaryButtonLabel?: string;
92
+ onTertiaryButtonClick?: () => void;
78
93
  dataTestId?: string;
79
94
  }
80
95
  ```
@@ -92,8 +107,9 @@ Pass `dataTestId="filters-drawer"` to get stable Playwright / Testing Library ho
92
107
  | `-close` | Header close (X) button |
93
108
  | `-body` | Main content area |
94
109
  | `-footer` | Footer action row |
95
- | `-primary` | Primary footer button |
96
- | `-secondary` | Secondary footer button |
110
+ | `-primary` | Primary footer button (solid) |
111
+ | `-secondary` | Secondary footer button (outline) |
112
+ | `-tertiary` | Tertiary footer button (ghost) |
97
113
 
98
114
  Header, title, close, footer, and button suffixes are omitted when those regions are not rendered.
99
115
 
@@ -144,15 +160,19 @@ const App = () => {
144
160
 
145
161
  ### With footer buttons
146
162
 
163
+ Footer actions render in a row: **tertiary** (ghost, left), **secondary** (outline), **primary** (solid, right). Omit any label to hide that button.
164
+
147
165
  ```jsx
148
166
  <Drawer
149
167
  isOpen={isOpen}
150
168
  onClose={() => setIsOpen(false)}
151
169
  title="Apply Filters"
152
- primaryButtonLabel="Apply"
153
- onPrimaryButtonClick={handleApply}
170
+ tertiaryButtonLabel="Learn more"
171
+ onTertiaryButtonClick={handleLearnMore}
154
172
  secondaryButtonLabel="Reset"
155
173
  onSecondaryButtonClick={handleReset}
174
+ primaryButtonLabel="Apply"
175
+ onPrimaryButtonClick={handleApply}
156
176
  >
157
177
  <Typography variant="p">Choose filter criteria.</Typography>
158
178
  </Drawer>
@@ -167,7 +187,10 @@ const App = () => {
167
187
  title="Settings"
168
188
  className="my-drawer-panel"
169
189
  overlayClassName="my-drawer-overlay"
190
+ contentClassName="my-drawer-content"
191
+ headerClassName="my-drawer-header"
170
192
  bodyClassName="my-drawer-body"
193
+ footerClassName="my-drawer-footer"
171
194
  dataTestId="settings-drawer"
172
195
  >
173
196
  ...
@@ -180,10 +203,12 @@ const App = () => {
180
203
  - **Mobile (<768px):** Always renders as a bottom sheet regardless of `placement`. Max height is **90dvh** with rounded top corners and safe-area padding at the bottom.
181
204
  - **Rendering:** Mounts when `isOpen` is true and stays mounted briefly after close so the exit transition can finish.
182
205
  - **Close:** `onClose` is called when the user clicks the X button (if `showCloseButton`), the overlay (if `closeOnOverlayClick`), or Escape (if `closeOnEscape`). Footer buttons do not auto-close; call `onClose` in their handlers if desired.
206
+ - **Footer buttons:** `primaryButtonLabel` (solid), `secondaryButtonLabel` (outline), and `tertiaryButtonLabel` (ghost). Tertiary aligns to the start of the footer row; secondary and primary align to the end.
183
207
  - **Body scroll:** Locked while open and restored on close.
184
208
  - **Focus:** Trapped while open; returned to the previously focused element on close.
185
209
  - **ARIA:** `role="dialog"`, `aria-modal="true"`, `aria-labelledby` when `title` is present, or `aria-label` via `ariaLabel`. Provide **`title` or `ariaLabel`** — a dev warning is logged when the drawer opens without either.
186
210
  - **Spacing:** `margin` uses the suffix API (e.g. `"0"`, `"b-2"`); component adds `m-` prefix.
211
+ - **Reduced motion:** Respects `prefers-reduced-motion: reduce` (transitions minimized in CSS).
187
212
 
188
213
  ## Related Components / Links
189
214
 
@@ -83,8 +83,10 @@ interface SelectProps {
83
83
  searchable?: boolean;
84
84
  /** Panel search field placeholder. @default "Search" */
85
85
  searchPlaceholder?: string;
86
- /** When search text matches nothing, optional “add” callback receives trimmed string. */
86
+ /** Persistent footer “add” action; callback receives trimmed search text or `""`. */
87
87
  onAddOption?: (value: string) => void;
88
+ /** Footer button label. @default "Add option" */
89
+ addOptionLabel?: string;
88
90
  /** @default true */
89
91
  closeOnAddOption?: boolean;
90
92
  placeholder?: string;
@@ -289,6 +291,7 @@ Pass **`dataTestId="fruit-select"`** on the field. The **root** id is on the fie
289
291
  | `-search-clear` | Clear panel search |
290
292
  | `-listbox` | Options list |
291
293
  | `-option-{value}` | Option row |
294
+ | `-add-option` | Footer add action (`onAddOption`) |
292
295
  | `-input` | Hidden `name` field (form submit) |
293
296
  | `-error` | Validation message |
294
297
 
package/llms.txt CHANGED
@@ -153,8 +153,8 @@ All component documentation is located in the `docs/` folder. The following docu
153
153
 
154
154
  ### Drawer Component
155
155
  - File: `docs/Drawer.md`
156
- - Purpose: Slide-in overlay panel from a configurable edge (left, right, top, bottom) on desktop; below 768px always renders as a bottom sheet (max 90dvh). Optional title, close button, footer actions; fade/slide transitions, focus trap, scroll lock.
157
- - Key Features: placement (left, right, top, bottom), size (small, medium, large, full), showCloseButton, closeOnOverlayClick, closeOnEscape, margin (suffix API), className/overlayClassName/contentClassName/headerClassName/bodyClassName/footerClassName, dataTestId with suffixed E2E hooks, ariaLabel
156
+ - Purpose: Slide-in overlay panel from a configurable edge (left, right, top, bottom) on desktop; below 768px always renders as a bottom sheet (max 90dvh). Optional title, close button, footer primary/secondary/tertiary actions; fade/slide transitions, focus trap, scroll lock.
157
+ - Key Features: placement (left, right, top, bottom), size (small, medium, large, full), showCloseButton, closeOnOverlayClick, closeOnEscape, margin (suffix API), className/overlayClassName/contentClassName/headerClassName/bodyClassName/footerClassName, footer buttons (primary solid, secondary outline, tertiary ghost), dataTestId with suffixed E2E hooks (`-primary`, `-secondary`, `-tertiary`), ariaLabel (dev warning when title and ariaLabel both omitted), mobile safe-area padding
158
158
  - Types: DrawerProps, DrawerPlacement, DrawerSize, DrawerMargin, SpacingOption
159
159
  - Related Components: Modal (centered dialog), BottomSheet (drag/snap from bottom), Button, Typography, Icon (used internally)
160
160
 
@@ -196,7 +196,7 @@ All component documentation is located in the `docs/` folder. The following docu
196
196
  ### FormControls
197
197
  - File: `docs/FormControls.md`
198
198
  - 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.
199
- - Key Features: Input (supports text/search/number + prefix/suffix; number maps to numeric text input, strips non-digits on change, optional `phoneDigits` trims autofill to last N digits for phone fields, 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
199
+ - Key Features: Input (supports text/search/number + prefix/suffix; number maps to numeric text input, strips non-digits on change, optional `phoneDigits` trims autofill to last N digits for phone fields, 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`** + **`addOptionLabel`** (persistent footer below scrollable list); 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
200
200
  - Types: InputProps, **Option** (preferred), **SelectOption** (deprecated alias of Option), **SelectValue**, SelectProps, TextAreaProps, CheckboxProps, CheckboxOption, CheckboxValue, FileProps, FileVariant, RadioProps, RadioOption, RadioValue, ToggleProps, DateProps, FormControlsStepperProps, **FormControlsStepperLayout**
201
201
  - 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.
202
202
  - Related Components: Pills (Input), Pagination (Select), Container, Button
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cleanplate",
3
- "version": "0.3.29",
3
+ "version": "0.3.30",
4
4
  "description": "CleanPlate - A Headless React UI Framework",
5
5
  "files": [
6
6
  "dist",