cleanplate 0.3.13 → 0.3.15
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/AGENTS.md +4 -0
- package/README.md +1 -1
- package/dist/components/form-controls/Input.d.ts +6 -0
- package/dist/components/form-controls/Input.d.ts.map +1 -1
- package/dist/components/menu-list/MenuList.d.ts +5 -1
- package/dist/components/menu-list/MenuList.d.ts.map +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.js +1 -1
- package/docs/FormControls.md +27 -3
- package/docs/MenuList.md +44 -1
- package/llms.txt +8 -4
- package/package.json +1 -1
package/docs/FormControls.md
CHANGED
|
@@ -6,7 +6,7 @@ FormControls is a set of form primitives exported as a namespace: `FormControls.
|
|
|
6
6
|
|
|
7
7
|
| Control | Purpose | Key props |
|
|
8
8
|
| --- | --- | --- |
|
|
9
|
-
| Input | Single-line text | placeholder, value, onChange(e), type |
|
|
9
|
+
| Input | Single-line text | placeholder, value, onChange(e), type, `phoneDigits` (numeric autofill) |
|
|
10
10
|
| TextArea | Multi-line text | placeholder, value, onChange(e) |
|
|
11
11
|
| Select | Floating UI combobox: desktop portalled list; **≤768px** bottom sheet; sync or async options, search, groups, multi chips + cap | `mode` / `isMulti`, `options`, `onSearch`, `searchable`, `groups`, `maxSelect`, `triggerMaxItems`, `panelMinWidth`, `name`, `placeholder`, `error` |
|
|
12
12
|
| Date | Calendar date picker (`date-fns` + Floating UI); **Cancel** / **OK** staging; desktop **popover** (~**400px** max width, viewport-capped); **≤768px** **bottom sheet** + backdrop; **month** / **year** subviews with back + titled headers; trigger **`calendar_month`** icon | `value`/`defaultValue` (`Date \| null`), `onChange`, `minDate`/`maxDate`/`disabledDates`/`disabledDaysOfWeek`, `locale`, `weekStartsOn`, `dateFormat`, `clearable`, `readOnly`, `name` (hidden **yyyy-MM-dd**), `popoverPlacement`, `onOpen`/`onClose` |
|
|
@@ -129,6 +129,12 @@ interface InputProps {
|
|
|
129
129
|
prefixA11yLabel?: string;
|
|
130
130
|
/** Spoken label for screen readers when `suffix` is a symbol/abbreviation. */
|
|
131
131
|
suffixA11yLabel?: string;
|
|
132
|
+
/**
|
|
133
|
+
* For `type="number"` only: after non-digits are removed, keep the last N digits
|
|
134
|
+
* when the value is longer — e.g. browser autofill `+91 98765 43210` → `9876543210`.
|
|
135
|
+
* Pair with `autoComplete="tel"` and `maxLength` for phone fields.
|
|
136
|
+
*/
|
|
137
|
+
phoneDigits?: number;
|
|
132
138
|
}
|
|
133
139
|
```
|
|
134
140
|
|
|
@@ -198,12 +204,29 @@ const [tags, setTags] = useState([{ label: "A", value: "a" }]);
|
|
|
198
204
|
|
|
199
205
|
```jsx
|
|
200
206
|
<FormControls.Input label="Amount" type="number" prefix="$" suffix="USD" placeholder="0.00" />
|
|
201
|
-
<FormControls.Input label="Phone" type="tel" prefix="+91" placeholder="98765 43210" />
|
|
202
207
|
<FormControls.Input label="Weight" type="number" suffix="kg" placeholder="0" />
|
|
203
208
|
<FormControls.Input label="Discount" type="number" suffix="%" placeholder="0" />
|
|
204
209
|
<FormControls.Input label="Website" type="url" suffix=".com" placeholder="acme" />
|
|
205
210
|
```
|
|
206
211
|
|
|
212
|
+
### Input — phone number (browser autofill)
|
|
213
|
+
|
|
214
|
+
Use `type="number"` (digit-only numeric input), `autoComplete="tel"` so the browser offers saved numbers, `prefix` for the visible country code, `phoneDigits={10}` to strip non-digits and keep the **last 10 digits** when autofill includes a country code (e.g. `+91 98765 43210` → `9876543210`), and `maxLength={10}` to cap manual entry.
|
|
215
|
+
|
|
216
|
+
```jsx
|
|
217
|
+
<FormControls.Input
|
|
218
|
+
label="Mobile number"
|
|
219
|
+
name="mobile"
|
|
220
|
+
type="number"
|
|
221
|
+
autoComplete="tel"
|
|
222
|
+
prefix="+91"
|
|
223
|
+
phoneDigits={10}
|
|
224
|
+
maxLength={10}
|
|
225
|
+
placeholder="10-digit mobile"
|
|
226
|
+
isRequired
|
|
227
|
+
/>
|
|
228
|
+
```
|
|
229
|
+
|
|
207
230
|
### TextArea and Date
|
|
208
231
|
|
|
209
232
|
```jsx
|
|
@@ -370,7 +393,8 @@ Pagination uses `FormControls.Select` for rows-per-page. Pills uses `FormControl
|
|
|
370
393
|
|
|
371
394
|
## Behavior Notes
|
|
372
395
|
|
|
373
|
-
- **Input (`type="number"`):** Renders as `<input type="text" inputmode="numeric" pattern="[0-9]*">` so the field shows the numeric keypad on mobile, validates digit-only input via HTML5 pattern, and avoids the well-known UX issues of native `type="number"` (scroll-wheel mutates value, spinner buttons, accepts `e`/`+`/`-`). Consumers still pass `type="number"` at the API boundary; for decimals or signed numbers, use `type="text"` and add a custom `inputMode`/validation.
|
|
396
|
+
- **Input (`type="number"`):** Renders as `<input type="text" inputmode="numeric" pattern="[0-9]*">` so the field shows the numeric keypad on mobile, validates digit-only input via HTML5 pattern, and avoids the well-known UX issues of native `type="number"` (scroll-wheel mutates value, spinner buttons, accepts `e`/`+`/`-`). Non-digits are stripped on `change` (covers browser autofill and paste). Consumers still pass `type="number"` at the API boundary; for decimals or signed numbers, use `type="text"` and add a custom `inputMode`/validation.
|
|
397
|
+
- **Input (`phoneDigits`):** Optional on `type="number"`. After non-digits are removed, values longer than `phoneDigits` are trimmed to the **last N digits** — use for phone fields when autofill inserts a country code (`+91 9876543210` → `9876543210` with `phoneDigits={10}`). Normalizes on `change` and `blur`. Pair with `autoComplete="tel"` and `maxLength`; show the country code via `prefix` (not in the value).
|
|
374
398
|
- **Input (`type="search"`):** Keeps `type="search"` semantics (mobile search keyboard, autosuggest history) but hides the browser's native cancel button and renders a leading `search` icon plus a custom `close` clear button from the icon library. The clear button shows only when the input has content, focuses the input on click, and emits a synthetic `onChange` with an empty value so both controlled (`value`/`onChange`) and uncontrolled (`defaultValue`) usage stay in sync.
|
|
375
399
|
- **Input (`prefix` / `suffix`):** Inline leading/trailing text affix for currency (`$`), country code (`+91`), unit (`kg`, `%`), TLD (`.com`), etc. Soft-capped at 4 characters so the layout stays predictable; longer strings are truncated. When set, the field's outer wrapper takes over the visible border / padding / focus ring so the affixes read as part of the same input. Affixes are linked to the input via `aria-describedby`, so screen readers announce e.g. "Amount, dollars, $500" when the visible affix is `$`. For symbols/abbreviations that don't read well, pass `prefixA11yLabel` / `suffixA11yLabel` (e.g. `prefix="$"`, `prefixA11yLabel="dollars"`). Ignored when `type="search"` (search already uses both edges) — for any other `type`, including `number`, affixes work as expected.
|
|
376
400
|
- **Input (validation / constraints):** `maxLength` is passed straight to the native attribute (works for any `type`). `min` / `max` are passed to the native attribute (HTML5 form-validation hints) and, for `type="number"` only, also clamped on `blur` — the user can finish typing freely and the value snaps to the bound when they leave the field.
|
package/docs/MenuList.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# MenuList Component
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Also known as:** **Tabs**, **tab bar**, **tab list** — CleanPlate does **not** ship a separate `Tabs` component. Use **MenuList** for in-page tab UIs (settings sections, filters, dashboard views). Pair a horizontal MenuList with your own panel content keyed off `activeItem`.
|
|
4
|
+
|
|
5
|
+
Purpose: Renders a list of navigational items with optional icons, active state highlighting, and customizable layout. Use for nav menus, sidebars, link lists, **and tab bars**. Supports horizontal and vertical directions, sizes (small, medium, large), variants (light, dark), and margin spacing. Items use Animated with fade-in-left on mount.
|
|
4
6
|
|
|
5
7
|
## Props / Inputs
|
|
6
8
|
|
|
@@ -97,8 +99,49 @@ const Nav = () => {
|
|
|
97
99
|
<MenuList items={items} size="large" activeItem={activeItem} onMenuClick={handleClick} />
|
|
98
100
|
```
|
|
99
101
|
|
|
102
|
+
### Tabs (in-page panels)
|
|
103
|
+
|
|
104
|
+
Use `direction="horizontal"`, controlled `activeItem`, and `onMenuClick` as the tab control. Render the active panel below (or beside) based on the same `activeItem` state. Do **not** import or invent a `Tabs` export — it does not exist in CleanPlate.
|
|
105
|
+
|
|
106
|
+
```jsx
|
|
107
|
+
import { useState } from "react";
|
|
108
|
+
import { MenuList, Container, Typography } from "cleanplate";
|
|
109
|
+
|
|
110
|
+
const TAB_ITEMS = [
|
|
111
|
+
{ label: "General", value: "general", icon: "settings" },
|
|
112
|
+
{ label: "Security", value: "security", icon: "lock" },
|
|
113
|
+
{ label: "Notifications", value: "notifications", icon: "notifications" },
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
const PANELS = {
|
|
117
|
+
general: <Typography variant="p">General settings content.</Typography>,
|
|
118
|
+
security: <Typography variant="p">Security settings content.</Typography>,
|
|
119
|
+
notifications: <Typography variant="p">Notification preferences.</Typography>,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const SettingsTabs = () => {
|
|
123
|
+
const [activeItem, setActiveItem] = useState("general");
|
|
124
|
+
return (
|
|
125
|
+
<>
|
|
126
|
+
<MenuList
|
|
127
|
+
items={TAB_ITEMS}
|
|
128
|
+
direction="horizontal"
|
|
129
|
+
variant="light"
|
|
130
|
+
activeItem={activeItem}
|
|
131
|
+
onMenuClick={(item) => setActiveItem(item.value)}
|
|
132
|
+
margin="b-2"
|
|
133
|
+
/>
|
|
134
|
+
<Container padding="4">{PANELS[activeItem]}</Container>
|
|
135
|
+
</>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
For URL-driven tabs, keep `activeItem` in sync with the route (e.g. search param or path segment) in the parent; MenuList stays presentational.
|
|
141
|
+
|
|
100
142
|
## Behavior Notes
|
|
101
143
|
|
|
144
|
+
- **No separate Tabs component:** Search terms like “tabs”, “tab bar”, or “TabList” map to **MenuList** + conditional panel content in the app.
|
|
102
145
|
- **Items:** Each item must have `label` and `value`. `icon` is optional (Material icon name).
|
|
103
146
|
- **onMenuClick:** Called with the clicked item. Use it to update `activeItem` or navigate.
|
|
104
147
|
- **DOM:** A `div` wrapping a `ul` of `li` elements; each `li` contains an anchor.
|
package/llms.txt
CHANGED
|
@@ -30,6 +30,9 @@ When generating code that uses CleanPlate components, **use component props inst
|
|
|
30
30
|
- **Container:** Use `padding`, `margin`, `gap`, `align`, `justify`, `display`, `width` props (spacing uses suffix rule above).
|
|
31
31
|
- **Button, Alert, Badge, etc.:** Each has documented props (e.g. variant, size, margin). Prefer these over inline style. Any `margin` or `padding` prop follows the suffix rule. See `docs/<ComponentName>.md` for the full prop list.
|
|
32
32
|
|
|
33
|
+
### Tabs (no separate component)
|
|
34
|
+
- CleanPlate does **not** export `Tabs`. For tab bars / in-page tab UIs, use **`MenuList`** (`docs/MenuList.md`): `direction="horizontal"`, `activeItem`, `onMenuClick`, then render panel content from the same `activeItem` state in the parent. Do not add a `Tabs` import or expect a `docs/Tabs.md` file.
|
|
35
|
+
|
|
33
36
|
### Example (correct)
|
|
34
37
|
```jsx
|
|
35
38
|
<Typography variant="h4" align="center" margin="b-2">Login</Typography>
|
|
@@ -179,7 +182,7 @@ All component documentation is located in the `docs/` folder. The following docu
|
|
|
179
182
|
### FormControls
|
|
180
183
|
- File: `docs/FormControls.md`
|
|
181
184
|
- 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
|
|
185
|
+
- 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
|
|
183
186
|
- 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
187
|
- 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.
|
|
185
188
|
- Related Components: Pills (Input), Pagination (Select), Container, Button
|
|
@@ -193,10 +196,11 @@ All component documentation is located in the `docs/` folder. The following docu
|
|
|
193
196
|
|
|
194
197
|
### MenuList Component
|
|
195
198
|
- File: `docs/MenuList.md`
|
|
196
|
-
-
|
|
199
|
+
- **Aliases (LLM / search):** **Tabs**, **tab bar**, **tab list** — there is no separate Tabs component; use MenuList + parent-rendered panels (see “Tabs (in-page panels)” in `docs/MenuList.md`).
|
|
200
|
+
- Purpose: Renders a list of navigational items with optional icons, active state highlighting, and customizable layout. Use for nav menus, sidebars, dropdown actions, **and horizontal tab bars** with conditional panel content below.
|
|
197
201
|
- Key Features: items (label, value, icon), activeItem, direction (horizontal, vertical), sizes (small, medium, large), variants (light, dark), margin (suffix API), onMenuClick(item), Animated entrance
|
|
198
202
|
- Types: MenuListProps, MenuListItem, MenuListSize, MenuListVariant, MenuListDirection, MenuListMargin
|
|
199
|
-
- Related Components: Dropdown (content), Header (navigation), Container, Typography, Icon, Animated (used internally)
|
|
203
|
+
- Related Components: Dropdown (content), Header (navigation), Container (tab panels + layout), Typography, Icon, Animated (used internally)
|
|
200
204
|
|
|
201
205
|
### Header Component
|
|
202
206
|
- File: `docs/Header.md`
|
|
@@ -268,7 +272,7 @@ All documentation files follow a consistent format:
|
|
|
268
272
|
| Animated | `docs/Animated.md` | Scroll-triggered entrance/exit animations |
|
|
269
273
|
| FormControls | `docs/FormControls.md` | Form primitives (Input, Select, TextArea, Date, Checkbox, **numeric Stepper**, etc.) |
|
|
270
274
|
| Toast | `docs/Toast.md` | Transient messages via ref.addMessage |
|
|
271
|
-
| MenuList | `docs/MenuList.md` | Nav menus
|
|
275
|
+
| MenuList | `docs/MenuList.md` | Nav menus, link lists, **tabs / tab bar** (no separate Tabs) |
|
|
272
276
|
| Header | `docs/Header.md` | Responsive navigation header with logo and menu |
|
|
273
277
|
| PageHeader | `docs/PageHeader.md` | Page title, subtitle, primary CTA, and more menu |
|
|
274
278
|
| BottomSheet | `docs/BottomSheet.md` | Slide-up panel for forms and actions |
|