@overdoser/react-toolkit 0.0.2 → 0.0.4
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 +107 -0
- package/README.md +8 -8
- package/components/Button/Button.d.ts +14 -0
- package/components/Dropdown/Dropdown.d.ts +36 -8
- package/components/Dropdown/DropdownItem.d.ts +5 -0
- package/components/Form/Form.d.ts +16 -0
- package/components/Form/FormField.d.ts +25 -0
- package/components/Form/FormRow.d.ts +1 -0
- package/components/Link/Link.d.ts +12 -0
- package/components/List/List.d.ts +16 -0
- package/components/Modal/Modal.d.ts +23 -0
- package/components/Popover/Popover.d.ts +18 -0
- package/components/Table/Table.d.ts +34 -4
- package/components/Typography/Typography.d.ts +10 -0
- package/components/inputs/Checkbox/Checkbox.d.ts +14 -0
- package/components/inputs/Input/Input.d.ts +10 -0
- package/components/inputs/Radio/Radio.d.ts +22 -0
- package/components/inputs/Select/Select.d.ts +27 -0
- package/components/inputs/Textarea/Textarea.d.ts +9 -0
- package/index.d.ts +14 -0
- package/llms.txt +478 -0
- package/manifest.json +360 -0
- package/package.json +1 -1
- package/recipes/confirm-modal.tsx +63 -0
- package/recipes/dropdown-menu.tsx +36 -0
- package/recipes/login-form.tsx +51 -0
- package/recipes/paginated-table.tsx +48 -0
- package/recipes/searchable-multi-select.tsx +42 -0
- package/recipes/server-side-table.tsx +92 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# AGENTS.md — @overdoser/react-toolkit
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding assistants integrating this library into a consumer project. Drop the relevant rules into your own project's AGENTS.md / CLAUDE.md / Cursor rules / Copilot instructions.
|
|
4
|
+
|
|
5
|
+
For exhaustive component reference (every prop, every variant, every signature), see `llms.txt` in this package. For machine-readable structured data, see `manifest.json`.
|
|
6
|
+
|
|
7
|
+
## Setup checklist
|
|
8
|
+
|
|
9
|
+
1. Install:
|
|
10
|
+
```bash
|
|
11
|
+
npm install @overdoser/react-toolkit
|
|
12
|
+
```
|
|
13
|
+
2. Install the optional peer **only if** you'll use `Form`/`FormField`:
|
|
14
|
+
```bash
|
|
15
|
+
npm install react-hook-form
|
|
16
|
+
```
|
|
17
|
+
3. Import the theme stylesheet **once** at the app entry point:
|
|
18
|
+
```ts
|
|
19
|
+
import '@overdoser/react-toolkit/theme.css';
|
|
20
|
+
```
|
|
21
|
+
Without this, components render unstyled.
|
|
22
|
+
|
|
23
|
+
## Import rules
|
|
24
|
+
|
|
25
|
+
- Always import from the package root: `import { Button } from '@overdoser/react-toolkit'`. Do **not** reach into subpaths (`@overdoser/react-toolkit/dist/...`) — they're not part of the public API.
|
|
26
|
+
- The only valid stylesheet subpath is `@overdoser/react-toolkit/theme.css`.
|
|
27
|
+
|
|
28
|
+
## Rules
|
|
29
|
+
|
|
30
|
+
1. **Use the toolkit components for UI affordances** — `Button`, `Input`, `Select`, `Textarea`, `Link`. Styling and a11y come for free.
|
|
31
|
+
2. **Inside `<Form>`, wrap every focusable input in `<FormField>`.** `FormField` clones the child to inject `value`/`onChange`/`name`/`error`/aria — let it.
|
|
32
|
+
3. **`<Modal>` content lives in `Modal.Header`, `Modal.Body`, `Modal.Footer`.** They own focus targeting and spacing.
|
|
33
|
+
4. **Inputs use `inputSize`** (`'sm' | 'md' | 'lg'`) for the toolkit size scale; the native `size` HTML attribute is independent.
|
|
34
|
+
5. **`Typography` `color`** accepts the preset tokens (`default | muted | primary | danger | success`). For arbitrary colors, use `style` or `className`.
|
|
35
|
+
6. **`Popover` content** goes through the `content` prop.
|
|
36
|
+
7. **Server-side `Table`:** providing `onSort` puts the table in controlled mode — the backend returns the page already sorted and sliced. Pass `pagination.totalRows` so the paginator can compute pages.
|
|
37
|
+
8. **Theme via CSS custom properties** (`--crk-*`) on `:root`. For finer overrides, use each component's `classes` prop — internal class names are hashed and unstable.
|
|
38
|
+
|
|
39
|
+
## Decision flow
|
|
40
|
+
|
|
41
|
+
- **Need a select?**
|
|
42
|
+
- Few options, native UX OK → `<Select options={...} />`.
|
|
43
|
+
- Many options, want a search filter → `<Select searchable options={...} />`.
|
|
44
|
+
- Multi-pick with chips → `<Select multiple options={...} onValuesChange={...} />`.
|
|
45
|
+
- You want it to look like a "menu trigger" instead of a form input → `<Dropdown options={...} value={...} onChange={...} />` (select-mode dropdown).
|
|
46
|
+
|
|
47
|
+
- **Need a menu (Edit / Delete / Archive)?** → `<Dropdown trigger={...}>` with `<DropdownItem>` children.
|
|
48
|
+
|
|
49
|
+
- **Need a tooltip / hover card / contextual panel?** → `<Popover trigger={...} content={...} />`.
|
|
50
|
+
|
|
51
|
+
- **Need a confirm prompt or a full dialog?** → `<Modal>` with `Modal.Header`/`Body`/`Footer`. Always provide either `aria-label` or use a `Modal.Header` (it's auto-wired as `aria-labelledby`).
|
|
52
|
+
|
|
53
|
+
- **Need a data table?** → `<Table>`.
|
|
54
|
+
- All data in memory → don't pass `onSort`; pass `pagination` (without `totalRows`) for client-side paging.
|
|
55
|
+
- Data from a server → pass `sortConfig` + `onSort`, and `pagination` with `totalRows`.
|
|
56
|
+
|
|
57
|
+
- **Multi-step / wizard form?** Use one `useForm()` per step OR a single `<Form>` wrapping all steps with conditional rendering. The toolkit doesn't ship a stepper.
|
|
58
|
+
|
|
59
|
+
## Common shapes (copy-paste)
|
|
60
|
+
|
|
61
|
+
### Wire a Select inside a Form
|
|
62
|
+
```tsx
|
|
63
|
+
<FormField name="role" label="Role">
|
|
64
|
+
<Select options={[
|
|
65
|
+
{ value: 'admin', label: 'Admin' },
|
|
66
|
+
{ value: 'member', label: 'Member' },
|
|
67
|
+
]} />
|
|
68
|
+
</FormField>
|
|
69
|
+
```
|
|
70
|
+
`FormField` bridges `onChange` → `onValueChange` automatically; just pass `options`.
|
|
71
|
+
|
|
72
|
+
### Wire a Checkbox inside a Form
|
|
73
|
+
```tsx
|
|
74
|
+
<FormField name="acceptTerms">
|
|
75
|
+
<Checkbox label="I accept the terms" />
|
|
76
|
+
</FormField>
|
|
77
|
+
```
|
|
78
|
+
`FormField` bridges the boolean form value to `checked`.
|
|
79
|
+
|
|
80
|
+
### Wire a RadioGroup inside a Form
|
|
81
|
+
```tsx
|
|
82
|
+
<FormField name="plan" label="Plan">
|
|
83
|
+
<RadioGroup name="plan" aria-label="Plan">
|
|
84
|
+
<Radio value="free" label="Free" />
|
|
85
|
+
<Radio value="pro" label="Pro" />
|
|
86
|
+
</RadioGroup>
|
|
87
|
+
</FormField>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Recipes
|
|
91
|
+
|
|
92
|
+
Full copy-paste-able files live alongside this doc. Each is one self-contained file that you can drop into a project. Adjust types/imports for your codebase.
|
|
93
|
+
|
|
94
|
+
- [recipes/login-form.tsx](./recipes/login-form.tsx) — Form + Input + Button with validation rules.
|
|
95
|
+
- [recipes/paginated-table.tsx](./recipes/paginated-table.tsx) — Client-side sortable + paginated table.
|
|
96
|
+
- [recipes/server-side-table.tsx](./recipes/server-side-table.tsx) — Server-driven sort & pagination.
|
|
97
|
+
- [recipes/confirm-modal.tsx](./recipes/confirm-modal.tsx) — Reusable destructive-action confirm dialog.
|
|
98
|
+
- [recipes/searchable-multi-select.tsx](./recipes/searchable-multi-select.tsx) — Multi-select with search and form integration.
|
|
99
|
+
- [recipes/dropdown-menu.tsx](./recipes/dropdown-menu.tsx) — Row action menu with `Dropdown` + `DropdownItem`.
|
|
100
|
+
|
|
101
|
+
## Anti-patterns to refuse
|
|
102
|
+
|
|
103
|
+
- "Wrap every component in a custom `Card` div for spacing" — spacing comes from the consumer's layout. Don't pollute toolkit components with wrapper divs.
|
|
104
|
+
- "Inline-style every component instead of using `classes` / `className`" — use `classes` for targeted overrides; reserve `style` for one-off layout tweaks.
|
|
105
|
+
- "Pass `loading` and `disabled` together on `Button`" — `loading` already implies disabled. Doubling up is harmless but redundant.
|
|
106
|
+
- "Mock `react-hook-form` to make `Form` work in tests" — use a real `useForm()` in your test wrappers; the toolkit's `FormField` reads context.
|
|
107
|
+
- "Manually call `e.preventDefault()` on `Form` submit" — `Form` already wraps `onSubmit` in `form.handleSubmit`. Just return data.
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @overdoser/react-toolkit
|
|
2
2
|
|
|
3
3
|
A modern, themeable React component library with SCSS modules.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @
|
|
8
|
+
npm install @overdoser/react-toolkit
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Setup
|
|
@@ -13,7 +13,7 @@ npm install @crk/react-toolkit
|
|
|
13
13
|
Import the stylesheet in your app entry point:
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
import '@
|
|
16
|
+
import '@overdoser/react-toolkit/theme.css';
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Theming
|
|
@@ -61,13 +61,13 @@ Override `--crk-*` CSS custom properties to customize the theme:
|
|
|
61
61
|
### Button
|
|
62
62
|
|
|
63
63
|
```tsx
|
|
64
|
-
import { Button } from '@
|
|
64
|
+
import { Button } from '@overdoser/react-toolkit';
|
|
65
65
|
|
|
66
66
|
<Button variant="primary" size="md" onClick={handleClick}>
|
|
67
67
|
Save
|
|
68
68
|
</Button>
|
|
69
69
|
|
|
70
|
-
<Button variant="danger" loading
|
|
70
|
+
<Button variant="danger" loading loadingStyle="dots">
|
|
71
71
|
Deleting...
|
|
72
72
|
</Button>
|
|
73
73
|
```
|
|
@@ -75,14 +75,14 @@ import { Button } from '@crk/react-toolkit';
|
|
|
75
75
|
### Form
|
|
76
76
|
|
|
77
77
|
```tsx
|
|
78
|
-
import { Form, FormField, Input, Button } from '@
|
|
78
|
+
import { Form, FormField, Input, Button } from '@overdoser/react-toolkit';
|
|
79
79
|
import { useForm } from 'react-hook-form';
|
|
80
80
|
|
|
81
81
|
function LoginForm() {
|
|
82
|
-
const
|
|
82
|
+
const form = useForm();
|
|
83
83
|
|
|
84
84
|
return (
|
|
85
|
-
<Form
|
|
85
|
+
<Form form={form} onSubmit={(values) => console.log(values)}>
|
|
86
86
|
<FormField name="email" label="Email">
|
|
87
87
|
<Input />
|
|
88
88
|
</FormField>
|
|
@@ -7,11 +7,25 @@ export interface ButtonClasses {
|
|
|
7
7
|
dot: string;
|
|
8
8
|
}
|
|
9
9
|
export interface ButtonProps extends ComponentPropsWithRef<'button'> {
|
|
10
|
+
/** Override class names on internal elements. */
|
|
10
11
|
classes?: Partial<ButtonClasses>;
|
|
12
|
+
/** Visual style. @default 'primary' */
|
|
11
13
|
variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
|
|
14
|
+
/** Button size. @default 'md' */
|
|
12
15
|
size?: 'sm' | 'md' | 'lg';
|
|
16
|
+
/** When true, the button is disabled and `aria-busy` is set. @default false */
|
|
13
17
|
loading?: boolean;
|
|
18
|
+
/** Loading animation style. Only applied when `loading` is true. @default 'dots' */
|
|
14
19
|
loadingStyle?: 'dots' | 'shimmer' | 'border';
|
|
20
|
+
/** Stretch the button to fill its container's width. @default false */
|
|
15
21
|
fullWidth?: boolean;
|
|
16
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Styled `<button>` with variants, sizes, and three loading-state animations.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* <Button variant="danger" size="lg" loading loadingStyle="shimmer" onClick={onDelete}>
|
|
28
|
+
* Delete
|
|
29
|
+
* </Button>
|
|
30
|
+
*/
|
|
17
31
|
export declare const Button: import('react').ForwardRefExoticComponent<Omit<ButtonProps, "ref"> & import('react').RefAttributes<HTMLButtonElement>>;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { CSSProperties } from 'react';
|
|
2
2
|
export interface DropdownOption {
|
|
3
|
+
/** Selected value sent through `onChange`. */
|
|
3
4
|
value: string;
|
|
5
|
+
/** Visible label in the menu and trigger. */
|
|
4
6
|
label: React.ReactNode;
|
|
7
|
+
/** When true, the option is non-interactive. */
|
|
5
8
|
disabled?: boolean;
|
|
6
9
|
}
|
|
7
10
|
export interface DropdownClasses {
|
|
@@ -13,28 +16,53 @@ export interface DropdownClasses {
|
|
|
13
16
|
item: string;
|
|
14
17
|
}
|
|
15
18
|
export interface DropdownProps {
|
|
16
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Trigger content for **menu mode**. Required when `options` is not provided.
|
|
21
|
+
* Ignored in **select mode** (when `options` is provided) — the trigger then
|
|
22
|
+
* shows the selected option's label.
|
|
23
|
+
*/
|
|
17
24
|
trigger?: React.ReactNode;
|
|
25
|
+
/** `<DropdownItem>` children for menu mode. */
|
|
18
26
|
children?: React.ReactNode;
|
|
27
|
+
/** Menu alignment relative to the trigger. @default 'left' */
|
|
19
28
|
align?: 'left' | 'right';
|
|
20
29
|
className?: string;
|
|
21
30
|
style?: CSSProperties;
|
|
22
31
|
id?: string;
|
|
32
|
+
/** Called when the menu opens. */
|
|
23
33
|
onOpen?: () => void;
|
|
34
|
+
/** Called when the menu closes (outside click, Escape, or selection). */
|
|
24
35
|
onClose?: () => void;
|
|
25
|
-
/**
|
|
36
|
+
/** Switches Dropdown to **select mode**: the trigger shows the selected option. */
|
|
26
37
|
options?: DropdownOption[];
|
|
27
|
-
/** Placeholder shown when no value is selected */
|
|
38
|
+
/** Placeholder shown in select mode when no value is selected. @default 'Select...' */
|
|
28
39
|
placeholder?: React.ReactNode;
|
|
29
|
-
/** Controlled value */
|
|
40
|
+
/** Controlled selected value (select mode). */
|
|
30
41
|
value?: string;
|
|
31
|
-
/**
|
|
42
|
+
/** Fires when an option is selected (select mode). */
|
|
32
43
|
onChange?: (value: string) => void;
|
|
33
|
-
/** Error
|
|
44
|
+
/** Error styling on the trigger. @default false */
|
|
34
45
|
error?: boolean;
|
|
35
|
-
/**
|
|
46
|
+
/** Stretch trigger and menu to container width. @default true */
|
|
36
47
|
fullWidth?: boolean;
|
|
37
|
-
/**
|
|
48
|
+
/** Override class names on internal elements. */
|
|
38
49
|
classes?: Partial<DropdownClasses>;
|
|
39
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Two-mode dropdown:
|
|
53
|
+
* - **Menu mode**: pass `trigger` + `<DropdownItem>` children for an action menu.
|
|
54
|
+
* - **Select mode**: pass `options` + `value` + `onChange` to use as a form input.
|
|
55
|
+
* In this mode the `trigger` prop is ignored.
|
|
56
|
+
*
|
|
57
|
+
* Closes on outside click and Escape; auto-focuses the first menu item on open.
|
|
58
|
+
*
|
|
59
|
+
* @example Menu mode
|
|
60
|
+
* <Dropdown trigger="More ▾">
|
|
61
|
+
* <DropdownItem onClick={onEdit}>Edit</DropdownItem>
|
|
62
|
+
* <DropdownItem onClick={onDelete}>Delete</DropdownItem>
|
|
63
|
+
* </Dropdown>
|
|
64
|
+
*
|
|
65
|
+
* @example Select mode
|
|
66
|
+
* <Dropdown options={options} value={value} onChange={setValue} />
|
|
67
|
+
*/
|
|
40
68
|
export declare const Dropdown: import('react').ForwardRefExoticComponent<DropdownProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { ComponentPropsWithRef } from 'react';
|
|
2
2
|
export interface DropdownItemProps extends ComponentPropsWithRef<'button'> {
|
|
3
|
+
/** Non-interactive when true. @default false */
|
|
3
4
|
disabled?: boolean;
|
|
4
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Action item rendered inside a `<Dropdown>` (menu mode).
|
|
8
|
+
* Renders as a `<button role="menuitem">`; supports all native button props.
|
|
9
|
+
*/
|
|
5
10
|
export declare const DropdownItem: import('react').ForwardRefExoticComponent<Omit<DropdownItemProps, "ref"> & import('react').RefAttributes<HTMLButtonElement>>;
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import { UseFormReturn, FieldValues, SubmitHandler } from 'react-hook-form';
|
|
2
2
|
export interface FormProps<T extends FieldValues> {
|
|
3
|
+
/** The result of `useForm()`. */
|
|
3
4
|
form: UseFormReturn<T>;
|
|
5
|
+
/** Called with validated values. `Form` handles `preventDefault` and validation internally. */
|
|
4
6
|
onSubmit: SubmitHandler<T>;
|
|
7
|
+
/** Top-of-form error messages. Rendered above children with `role="alert"`. */
|
|
5
8
|
errors?: React.ReactNode[];
|
|
6
9
|
className?: string;
|
|
7
10
|
style?: React.CSSProperties;
|
|
8
11
|
children: React.ReactNode;
|
|
9
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* react-hook-form wrapper that provides `FormProvider` and a styled `<form>` element.
|
|
15
|
+
*
|
|
16
|
+
* Pair with `<FormField>` for each input — `FormField` reads the form context
|
|
17
|
+
* via `useFormContext` and clones its child to inject `value`/`onChange`/`name`/`error`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const form = useForm<Values>();
|
|
21
|
+
* <Form form={form} onSubmit={(values) => save(values)}>
|
|
22
|
+
* <FormField name="email" label="Email"><Input /></FormField>
|
|
23
|
+
* <Button type="submit">Save</Button>
|
|
24
|
+
* </Form>
|
|
25
|
+
*/
|
|
10
26
|
export declare function Form<T extends FieldValues>({ form, onSubmit, errors, className, style, children, }: FormProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -6,14 +6,39 @@ export interface FormFieldClasses {
|
|
|
6
6
|
helperText: string;
|
|
7
7
|
}
|
|
8
8
|
export interface FormFieldProps {
|
|
9
|
+
/** Field name. Becomes the field `id` and the react-hook-form path. */
|
|
9
10
|
name: string;
|
|
11
|
+
/** Field label rendered above the input. */
|
|
10
12
|
label?: ReactNode;
|
|
13
|
+
/** Helper text rendered below the input (replaced by error message when invalid). */
|
|
11
14
|
helperText?: ReactNode;
|
|
15
|
+
/** Renders a `*` indicator next to the label. Does NOT add validation rules — pass those in `rules`. */
|
|
12
16
|
required?: boolean;
|
|
17
|
+
/** react-hook-form `useController` rules (e.g., `{ required: 'msg', minLength: { value: 8, message: '…' } }`). */
|
|
13
18
|
rules?: Record<string, unknown>;
|
|
19
|
+
/** Override class names on internal elements. */
|
|
14
20
|
classes?: Partial<FormFieldClasses>;
|
|
15
21
|
className?: string;
|
|
16
22
|
style?: CSSProperties;
|
|
23
|
+
/**
|
|
24
|
+
* Exactly one input element. `FormField` clones it to inject
|
|
25
|
+
* `value`/`onChange`/`name`/`id`/`error`/aria props — do NOT pass those manually.
|
|
26
|
+
*
|
|
27
|
+
* Supported: `Input`, `Textarea`, `Select`, `Dropdown` (select-mode), `Checkbox`, `Radio`/`RadioGroup`.
|
|
28
|
+
*/
|
|
17
29
|
children: ReactElement;
|
|
18
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Bridges react-hook-form to a single child input via `cloneElement`.
|
|
33
|
+
* Must be used inside a `<Form>` (which provides `FormProvider`).
|
|
34
|
+
*
|
|
35
|
+
* Bridges automatically applied:
|
|
36
|
+
* - `Select` (multi/searchable) and `Dropdown` (select-mode): `onChange` → `onValueChange`/`onValuesChange`.
|
|
37
|
+
* - `Checkbox`: when the form value is boolean, it's bridged to `checked`.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* <FormField name="email" label="Email" required rules={{ required: 'Required' }}>
|
|
41
|
+
* <Input type="email" />
|
|
42
|
+
* </FormField>
|
|
43
|
+
*/
|
|
19
44
|
export declare function FormField({ name, label, helperText, required, rules, classes, className, style, children, }: FormFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,4 +4,5 @@ export interface FormRowProps {
|
|
|
4
4
|
className?: string;
|
|
5
5
|
style?: CSSProperties;
|
|
6
6
|
}
|
|
7
|
+
/** Horizontal flex row that lays out its children — useful for putting two `<FormField>`s side-by-side. */
|
|
7
8
|
export declare function FormRow({ children, className, style }: FormRowProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { ComponentPropsWithRef } from 'react';
|
|
2
2
|
export interface LinkProps extends ComponentPropsWithRef<'a'> {
|
|
3
|
+
/** Visual style. @default 'default' */
|
|
3
4
|
variant?: 'default' | 'muted' | 'danger';
|
|
5
|
+
/**
|
|
6
|
+
* Mark the link as opening in a new tab.
|
|
7
|
+
* Sets `target="_blank"` and `rel="noopener noreferrer"`,
|
|
8
|
+
* and appends a visually-hidden " (opens in a new tab)" suffix for screen readers.
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
4
11
|
external?: boolean;
|
|
5
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Styled `<a>` with variants and safe external-link defaults.
|
|
15
|
+
*
|
|
16
|
+
* @example <Link href="https://example.com" external>Docs</Link>
|
|
17
|
+
*/
|
|
6
18
|
export declare const Link: import('react').ForwardRefExoticComponent<Omit<LinkProps, "ref"> & import('react').RefAttributes<HTMLAnchorElement>>;
|
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { ComponentPropsWithRef } from 'react';
|
|
2
2
|
export interface ListProps extends ComponentPropsWithRef<'ul'> {
|
|
3
|
+
/**
|
|
4
|
+
* List style. `'ordered'` renders `<ol>`, `'unordered'` renders `<ul>`,
|
|
5
|
+
* `'none'` renders `<ul>` without bullets.
|
|
6
|
+
* @default 'unordered'
|
|
7
|
+
*/
|
|
3
8
|
variant?: 'unordered' | 'ordered' | 'none';
|
|
9
|
+
/** Vertical spacing between items. @default 'md' */
|
|
4
10
|
spacing?: 'sm' | 'md' | 'lg';
|
|
5
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Themed list (`<ul>` or `<ol>` based on `variant`).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* <List variant="ordered" spacing="lg">
|
|
17
|
+
* <ListItem>First</ListItem>
|
|
18
|
+
* <ListItem>Second</ListItem>
|
|
19
|
+
* </List>
|
|
20
|
+
*/
|
|
6
21
|
export declare const List: import('react').ForwardRefExoticComponent<Omit<ListProps, "ref"> & import('react').RefAttributes<HTMLOListElement | HTMLUListElement>>;
|
|
7
22
|
export interface ListItemProps extends ComponentPropsWithRef<'li'> {
|
|
8
23
|
}
|
|
24
|
+
/** Themed `<li>`. Use only inside `<List>`. */
|
|
9
25
|
export declare const ListItem: import('react').ForwardRefExoticComponent<Omit<ListItemProps, "ref"> & import('react').RefAttributes<HTMLLIElement>>;
|
|
@@ -11,6 +11,7 @@ export interface ModalHeaderProps {
|
|
|
11
11
|
children: ReactNode;
|
|
12
12
|
className?: string;
|
|
13
13
|
style?: CSSProperties;
|
|
14
|
+
/** When provided, renders an "×" close button in the header. */
|
|
14
15
|
onClose?: () => void;
|
|
15
16
|
}
|
|
16
17
|
export interface ModalBodyProps {
|
|
@@ -27,15 +28,23 @@ declare const Header: FC<ModalHeaderProps>;
|
|
|
27
28
|
declare const Body: FC<ModalBodyProps>;
|
|
28
29
|
declare const Footer: FC<ModalFooterProps>;
|
|
29
30
|
export interface ModalProps {
|
|
31
|
+
/** Whether the modal is visible. */
|
|
30
32
|
open: boolean;
|
|
33
|
+
/** Called when the user requests close (backdrop, Escape, or `Modal.Header` close button). */
|
|
31
34
|
onClose: () => void;
|
|
35
|
+
/** Close when the backdrop is clicked. @default true */
|
|
32
36
|
closeOnBackdrop?: boolean;
|
|
37
|
+
/** Close when Escape is pressed. @default true */
|
|
33
38
|
closeOnEscape?: boolean;
|
|
39
|
+
/** Modal size. `'fullscreen'` covers the viewport. @default 'md' */
|
|
34
40
|
size?: 'sm' | 'md' | 'lg' | 'fullscreen';
|
|
41
|
+
/** Override class names on internal elements. */
|
|
35
42
|
classes?: Partial<ModalClasses>;
|
|
36
43
|
className?: string;
|
|
37
44
|
style?: CSSProperties;
|
|
45
|
+
/** Should be `<Modal.Header>`, `<Modal.Body>`, and `<Modal.Footer>`. */
|
|
38
46
|
children: ReactNode;
|
|
47
|
+
/** Accessible label. Use this OR `aria-labelledby`. If neither is set, `Modal.Header` is auto-wired as `aria-labelledby`. */
|
|
39
48
|
'aria-label'?: string;
|
|
40
49
|
'aria-labelledby'?: string;
|
|
41
50
|
}
|
|
@@ -44,5 +53,19 @@ type ModalComponent = ReturnType<typeof forwardRef<HTMLDivElement, ModalProps>>
|
|
|
44
53
|
Body: typeof Body;
|
|
45
54
|
Footer: typeof Footer;
|
|
46
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* Portal-based modal with focus trap, body scroll lock, and Escape/backdrop close.
|
|
58
|
+
* Compose with `Modal.Header`, `Modal.Body`, and `Modal.Footer`.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* <Modal open={open} onClose={() => setOpen(false)} size="md">
|
|
62
|
+
* <Modal.Header onClose={() => setOpen(false)}>Title</Modal.Header>
|
|
63
|
+
* <Modal.Body>Content</Modal.Body>
|
|
64
|
+
* <Modal.Footer>
|
|
65
|
+
* <Button variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
|
|
66
|
+
* <Button variant="primary" onClick={confirm}>OK</Button>
|
|
67
|
+
* </Modal.Footer>
|
|
68
|
+
* </Modal>
|
|
69
|
+
*/
|
|
47
70
|
export declare const Modal: ModalComponent;
|
|
48
71
|
export {};
|
|
@@ -5,14 +5,32 @@ export interface PopoverClasses {
|
|
|
5
5
|
popover: string;
|
|
6
6
|
}
|
|
7
7
|
export interface PopoverProps {
|
|
8
|
+
/** Trigger content. Wrapped in an internal `<button>`. */
|
|
8
9
|
trigger: React.ReactNode;
|
|
10
|
+
/** Panel content. Rendered inside a `[role="dialog"]` when open. */
|
|
9
11
|
content: React.ReactNode;
|
|
12
|
+
/** Side of the trigger to anchor the panel to. @default 'bottom' */
|
|
10
13
|
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
14
|
+
/** Controlled open state. */
|
|
11
15
|
open?: boolean;
|
|
16
|
+
/** Called when the open state changes. */
|
|
12
17
|
onOpenChange?: (open: boolean) => void;
|
|
18
|
+
/** Override class names on internal elements. */
|
|
13
19
|
classes?: Partial<PopoverClasses>;
|
|
14
20
|
className?: string;
|
|
15
21
|
style?: CSSProperties;
|
|
22
|
+
/** Use the `content` prop. */
|
|
16
23
|
children?: never;
|
|
17
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Anchored popover panel. Closes on outside click and Escape; auto-focuses
|
|
27
|
+
* the first focusable element inside `content` when opened.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* <Popover
|
|
31
|
+
* trigger="Help"
|
|
32
|
+
* content={<p>Some help text</p>}
|
|
33
|
+
* position="right"
|
|
34
|
+
* />
|
|
35
|
+
*/
|
|
18
36
|
export declare const Popover: import('react').ForwardRefExoticComponent<PopoverProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { SortConfig } from './useTableSort';
|
|
3
3
|
export interface ColumnDef<T> {
|
|
4
|
+
/** Object key on each row to read the cell value from. */
|
|
4
5
|
key: keyof T & string;
|
|
6
|
+
/** Header content (string or any ReactNode). */
|
|
5
7
|
header: ReactNode;
|
|
8
|
+
/** Enable click-to-sort on the header. */
|
|
6
9
|
sortable?: boolean;
|
|
10
|
+
/** Custom cell renderer; defaults to rendering `row[key]`. */
|
|
7
11
|
render?: (value: T[keyof T], row: T, index: number) => ReactNode;
|
|
12
|
+
/** Column width (CSS value). */
|
|
8
13
|
width?: string | number;
|
|
14
|
+
/** Text alignment for cells in this column. */
|
|
9
15
|
align?: 'left' | 'center' | 'right';
|
|
10
16
|
}
|
|
11
17
|
export interface PaginationConfig {
|
|
@@ -31,25 +37,49 @@ export interface TableClasses {
|
|
|
31
37
|
pageButton: string;
|
|
32
38
|
}
|
|
33
39
|
export interface TableProps<T extends Record<string, unknown>> {
|
|
40
|
+
/** Row data. */
|
|
34
41
|
data: T[];
|
|
42
|
+
/** Column definitions. */
|
|
35
43
|
columns: ColumnDef<T>[];
|
|
36
|
-
/** Controlled sort config
|
|
44
|
+
/** Controlled sort config. Pair with `onSort` for server-side sorting. */
|
|
37
45
|
sortConfig?: SortConfig[];
|
|
38
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Sort callback. When provided, Table is in **controlled** mode and will
|
|
48
|
+
* NOT sort `data` internally and will NOT slice for pagination — your
|
|
49
|
+
* server is expected to handle both.
|
|
50
|
+
*/
|
|
39
51
|
onSort?: (config: SortConfig[]) => void;
|
|
40
|
-
/** Enable multi-column sort via Ctrl
|
|
52
|
+
/** Enable multi-column sort via Ctrl/Cmd-click on a sortable header. @default true */
|
|
41
53
|
multiSort?: boolean;
|
|
54
|
+
/** Alternate row backgrounds. @default false */
|
|
42
55
|
striped?: boolean;
|
|
56
|
+
/** Highlight rows on hover. @default false */
|
|
43
57
|
hoverable?: boolean;
|
|
58
|
+
/** Tighter row padding. @default false */
|
|
44
59
|
compact?: boolean;
|
|
45
60
|
className?: string;
|
|
46
61
|
style?: React.CSSProperties;
|
|
62
|
+
/** Column key to use as React `key` per row. Falls back to row index. */
|
|
47
63
|
rowKey?: keyof T & string;
|
|
64
|
+
/** Content rendered when `data` is empty. @default 'No data' */
|
|
48
65
|
emptyMessage?: ReactNode;
|
|
49
|
-
/**
|
|
66
|
+
/** When provided, renders a paginator below the table. */
|
|
50
67
|
pagination?: PaginationConfig;
|
|
68
|
+
/** Override class names on internal elements. */
|
|
51
69
|
classes?: Partial<TableClasses>;
|
|
52
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Generic data table with sortable columns, multi-sort (Ctrl/Cmd-click), and
|
|
73
|
+
* client- or server-side pagination.
|
|
74
|
+
*
|
|
75
|
+
* Sort cycle on header click: `none → asc → desc → none`.
|
|
76
|
+
*
|
|
77
|
+
* **Client-side mode** (default): omit `onSort`. Table sorts and paginates internally.
|
|
78
|
+
*
|
|
79
|
+
* **Controlled mode**: pass `sortConfig` + `onSort`. Your server returns the
|
|
80
|
+
* page already sorted and sliced; pass `pagination.totalRows` so the
|
|
81
|
+
* paginator can compute page count.
|
|
82
|
+
*/
|
|
53
83
|
declare function TableInner<T extends Record<string, unknown>>({ data, columns, sortConfig: controlledSortConfig, onSort, multiSort, striped, hoverable, compact, className, style, rowKey, emptyMessage, pagination, classes, }: TableProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
54
84
|
export declare const Table: typeof TableInner;
|
|
55
85
|
export {};
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { ComponentPropsWithRef } from 'react';
|
|
2
2
|
type TypographyVariant = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'label';
|
|
3
3
|
export interface TypographyProps extends Omit<ComponentPropsWithRef<'p'>, 'color'> {
|
|
4
|
+
/** HTML tag to render. @default 'p' */
|
|
4
5
|
variant?: TypographyVariant;
|
|
6
|
+
/** Font weight. */
|
|
5
7
|
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
8
|
+
/** Preset color token. For arbitrary colors, use `style` or `className`. */
|
|
6
9
|
color?: 'default' | 'muted' | 'primary' | 'danger' | 'success';
|
|
10
|
+
/** Text alignment. */
|
|
7
11
|
align?: 'left' | 'center' | 'right';
|
|
12
|
+
/** Single-line ellipsis truncation. @default false */
|
|
8
13
|
truncate?: boolean;
|
|
9
14
|
children: React.ReactNode;
|
|
10
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Renders `h1`–`h6`, `p`, `span`, or `label` with theme-aware typography tokens.
|
|
18
|
+
*
|
|
19
|
+
* @example <Typography variant="h2" weight="bold" color="primary">Title</Typography>
|
|
20
|
+
*/
|
|
11
21
|
export declare const Typography: import('react').ForwardRefExoticComponent<Omit<TypographyProps, "ref"> & import('react').RefAttributes<HTMLElement>>;
|
|
12
22
|
export {};
|
|
@@ -5,8 +5,22 @@ export interface CheckboxClasses {
|
|
|
5
5
|
label: string;
|
|
6
6
|
}
|
|
7
7
|
export interface CheckboxProps extends ComponentPropsWithRef<'input'> {
|
|
8
|
+
/** Visible label rendered next to the box. */
|
|
8
9
|
label?: ReactNode;
|
|
10
|
+
/** Sets the DOM `indeterminate` property; visually distinct from checked/unchecked. @default false */
|
|
9
11
|
indeterminate?: boolean;
|
|
12
|
+
/** Override class names on internal elements. */
|
|
10
13
|
classes?: Partial<CheckboxClasses>;
|
|
11
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Themed checkbox. The `<input>` is wrapped in a `<label>` so clicking the
|
|
17
|
+
* label toggles the input.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* <Checkbox
|
|
21
|
+
* label="Remember me"
|
|
22
|
+
* checked={remember}
|
|
23
|
+
* onChange={(e) => setRemember(e.target.checked)}
|
|
24
|
+
* />
|
|
25
|
+
*/
|
|
12
26
|
export declare const Checkbox: import('react').ForwardRefExoticComponent<Omit<CheckboxProps, "ref"> & import('react').RefAttributes<HTMLInputElement>>;
|
|
@@ -6,10 +6,20 @@ export interface InputClasses {
|
|
|
6
6
|
suffix: string;
|
|
7
7
|
}
|
|
8
8
|
export interface InputProps extends Omit<ComponentPropsWithRef<'input'>, 'prefix'> {
|
|
9
|
+
/** Toolkit size scale (independent of the native `size` HTML attribute). @default 'md' */
|
|
9
10
|
inputSize?: 'sm' | 'md' | 'lg';
|
|
11
|
+
/** Apply error styling. @default false */
|
|
10
12
|
error?: boolean;
|
|
13
|
+
/** Content rendered inside the input wrapper, before the field. */
|
|
11
14
|
prefix?: ReactNode;
|
|
15
|
+
/** Content rendered inside the input wrapper, after the field. */
|
|
12
16
|
suffix?: ReactNode;
|
|
17
|
+
/** Override class names on internal elements. */
|
|
13
18
|
classes?: Partial<InputClasses>;
|
|
14
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Themed text input with optional `prefix`/`suffix` slots.
|
|
22
|
+
*
|
|
23
|
+
* @example <Input type="email" inputSize="lg" prefix="@" placeholder="username" />
|
|
24
|
+
*/
|
|
15
25
|
export declare const Input: import('react').ForwardRefExoticComponent<Omit<InputProps, "ref"> & import('react').RefAttributes<HTMLInputElement>>;
|