@paul-portfolio/react 0.1.14 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -0
- package/dist/Avatar.d.ts +8 -0
- package/dist/Avatar.js +5 -0
- package/dist/Badge.d.ts +8 -0
- package/dist/Badge.js +5 -0
- package/dist/Button.d.ts +19 -0
- package/dist/Button.js +11 -0
- package/dist/Card.d.ts +14 -0
- package/dist/Card.js +16 -0
- package/dist/Chip.d.ts +10 -0
- package/dist/Chip.js +8 -0
- package/dist/Divider.d.ts +10 -0
- package/dist/Divider.js +9 -0
- package/dist/IconButton.d.ts +12 -0
- package/dist/IconButton.js +10 -0
- package/dist/InfoTip.d.ts +13 -0
- package/dist/InfoTip.js +9 -0
- package/dist/Input.d.ts +8 -0
- package/dist/Input.js +9 -0
- package/dist/Modal.d.ts +23 -0
- package/dist/Modal.js +31 -0
- package/dist/Skeleton.d.ts +7 -0
- package/dist/Skeleton.js +11 -0
- package/dist/Spinner.d.ts +12 -0
- package/dist/Spinner.js +9 -0
- package/dist/Switch.d.ts +13 -0
- package/dist/Switch.js +11 -0
- package/dist/Textarea.d.ts +11 -0
- package/dist/Textarea.js +13 -0
- package/dist/Tooltip.d.ts +8 -0
- package/dist/Tooltip.js +8 -0
- package/dist/VisuallyHidden.d.ts +6 -0
- package/dist/VisuallyHidden.js +4 -0
- package/dist/cx.d.ts +1 -0
- package/dist/cx.js +3 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +17 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# @paul-portfolio/react
|
|
2
|
+
|
|
3
|
+
React components for the Paul Design System. Thin, accessible components styled
|
|
4
|
+
by [`@paul-portfolio/css`](https://www.npmjs.com/package/@paul-portfolio/css) —
|
|
5
|
+
the components render semantic markup with class names, and the CSS package
|
|
6
|
+
supplies the looks via design tokens.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @paul-portfolio/react @paul-portfolio/css @paul-portfolio/tokens
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`@paul-portfolio/css` and `@paul-portfolio/tokens` are peer dependencies, along
|
|
15
|
+
with `react` and `react-dom` (>=18).
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
Import the token variables and the component styles once, near the root of your
|
|
20
|
+
app:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import "@paul-portfolio/tokens/tokens.css";
|
|
24
|
+
import "@paul-portfolio/css/components.css";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then use the components:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { Button } from "@paul-portfolio/react";
|
|
31
|
+
|
|
32
|
+
export function Example() {
|
|
33
|
+
return <Button variant="primary">Save</Button>;
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Components
|
|
38
|
+
|
|
39
|
+
### Button
|
|
40
|
+
|
|
41
|
+
Variants (`primary`, `secondary`, `outline`, `ghost`, `danger`), sizes (`xs`,
|
|
42
|
+
`sm`, `md`, `lg`), `loading`, and an `href` form that renders an `<a>`.
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
<Button variant="outline" size="sm" onClick={save}>Save</Button>
|
|
46
|
+
<Button href="/docs" variant="ghost">Docs</Button>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### IconButton
|
|
50
|
+
|
|
51
|
+
A square, icon-only button. Requires `aria-label` since there's no visible text.
|
|
52
|
+
Sizes `sm` and `md`.
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
<IconButton aria-label="Close" onClick={close}>✕</IconButton>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Input / Textarea
|
|
59
|
+
|
|
60
|
+
Labelled fields with `error` and `helper` text. `Input` takes a `size`
|
|
61
|
+
(`sm`/`md`); `Textarea` resizes vertically.
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
<Input label="Email" type="email" error={emailError} />
|
|
65
|
+
<Textarea label="Bio" helper="Max 200 characters" />
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Switch
|
|
69
|
+
|
|
70
|
+
An on/off toggle. Controlled via `checked` + `onCheckedChange`. Give it an
|
|
71
|
+
`aria-label`.
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<Switch checked={on} onCheckedChange={setOn} aria-label="Notifications" />
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Chip
|
|
78
|
+
|
|
79
|
+
A compact tag, optionally removable and colorable.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<Chip label="Design" onRemove={() => remove("design")} />
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Modal
|
|
86
|
+
|
|
87
|
+
An accessible dialog with focus trap, Escape-to-close, and a backdrop. Controlled
|
|
88
|
+
via `open` + `onClose`.
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
<Modal open={open} onClose={close} aria-label="Settings">
|
|
92
|
+
<h2>Settings</h2>
|
|
93
|
+
</Modal>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Tooltip / InfoTip
|
|
97
|
+
|
|
98
|
+
`Tooltip` wraps any element and shows text on hover. `InfoTip` is the common
|
|
99
|
+
"small i that explains a label" shortcut, built on `Tooltip`.
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
<Tooltip content="Copied to clipboard"><IconButton aria-label="Copy">⧉</IconButton></Tooltip>
|
|
103
|
+
<InfoTip content="We never share your email." />
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Card
|
|
107
|
+
|
|
108
|
+
A surface with optional `Card.Header`, `Card.Body`, and `Card.Footer`.
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
<Card>
|
|
112
|
+
<Card.Header>Plan</Card.Header>
|
|
113
|
+
<Card.Body>Pro — $12/mo</Card.Body>
|
|
114
|
+
</Card>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Badge / Avatar / Skeleton / Spinner
|
|
118
|
+
|
|
119
|
+
- **Badge** — a small status label.
|
|
120
|
+
- **Avatar** — a rounded user image with initials fallback.
|
|
121
|
+
- **Skeleton** — a shimmer placeholder for content that's still loading.
|
|
122
|
+
- **Spinner** — an indeterminate loading spinner (`sm`/`md`/`lg`), announced as a
|
|
123
|
+
status region.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
<Badge>New</Badge>
|
|
127
|
+
<Avatar name="Ada Lovelace" src={url} />
|
|
128
|
+
<Skeleton width="12rem" />
|
|
129
|
+
<Spinner label="Loading results" />
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Divider
|
|
133
|
+
|
|
134
|
+
A thin separator rule, `horizontal` (default) or `vertical`.
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
<Divider />
|
|
138
|
+
<Divider orientation="vertical" />
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### VisuallyHidden
|
|
142
|
+
|
|
143
|
+
Renders content that's available to screen readers but hidden visually.
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
<VisuallyHidden>Loading</VisuallyHidden>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### cx
|
|
150
|
+
|
|
151
|
+
A tiny classname joiner used internally, exported for convenience.
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
cx("card", isActive && "card--active"); // "card card--active"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Accessibility
|
|
158
|
+
|
|
159
|
+
Every component ships with an axe test in the package's suite. Components that
|
|
160
|
+
have no visible text (IconButton, Switch, Spinner) require or default an
|
|
161
|
+
accessible name, and interactive components expose the right roles and ARIA
|
|
162
|
+
state.
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT © Paul Sumido
|
package/dist/Avatar.d.ts
ADDED
package/dist/Avatar.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './cx';
|
|
3
|
+
export function Avatar({ src, alt, size, fallback }) {
|
|
4
|
+
return (_jsx("div", { className: cx('avatar', size && `avatar--${size}`), children: src ? (_jsx("img", { src: src, alt: alt })) : (_jsx("span", { className: "avatar--fallback", children: fallback })) }));
|
|
5
|
+
}
|
package/dist/Badge.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
type BadgeProps = {
|
|
3
|
+
variant?: 'success' | 'warning' | 'error' | 'info';
|
|
4
|
+
dot?: boolean;
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
};
|
|
7
|
+
export declare function Badge({ variant, dot, children }: BadgeProps): import("react").JSX.Element;
|
|
8
|
+
export {};
|
package/dist/Badge.js
ADDED
package/dist/Button.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type AnchorHTMLAttributes, type ReactNode } from 'react';
|
|
2
|
+
type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
|
|
3
|
+
type ButtonSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
4
|
+
type BaseProps = {
|
|
5
|
+
variant?: ButtonVariant;
|
|
6
|
+
size?: ButtonSize;
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
};
|
|
11
|
+
type ButtonAsButton = BaseProps & Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof BaseProps> & {
|
|
12
|
+
href?: never;
|
|
13
|
+
};
|
|
14
|
+
type ButtonAsAnchor = BaseProps & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps> & {
|
|
15
|
+
href: string;
|
|
16
|
+
};
|
|
17
|
+
type ButtonProps = ButtonAsButton | ButtonAsAnchor;
|
|
18
|
+
export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
|
|
19
|
+
export {};
|
package/dist/Button.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, } from 'react';
|
|
3
|
+
import { cx } from './cx';
|
|
4
|
+
export const Button = forwardRef(function Button({ variant, size, loading, disabled, className, children, ...props }, ref) {
|
|
5
|
+
const classes = cx('btn', variant && `btn--${variant}`, size && size !== 'md' && `btn--${size}`, className);
|
|
6
|
+
if ('href' in props && props.href) {
|
|
7
|
+
return (_jsx("a", { ref: ref, className: classes, "aria-disabled": disabled || loading || undefined, ...props, children: children }));
|
|
8
|
+
}
|
|
9
|
+
const { href: _, ...buttonProps } = props;
|
|
10
|
+
return (_jsx("button", { ref: ref, className: classes, disabled: disabled || loading, "aria-disabled": disabled || loading || undefined, "aria-busy": loading || undefined, ...buttonProps, onClick: disabled || loading ? undefined : buttonProps.onClick, children: children }));
|
|
11
|
+
});
|
package/dist/Card.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReactNode, type HTMLAttributes } from 'react';
|
|
2
|
+
type CardVariant = 'elevated' | 'interactive';
|
|
3
|
+
declare function Header({ className, children, ...props }: HTMLAttributes<HTMLDivElement>): import("react").JSX.Element;
|
|
4
|
+
declare function Body({ className, children, ...props }: HTMLAttributes<HTMLDivElement>): import("react").JSX.Element;
|
|
5
|
+
declare function Footer({ className, children, ...props }: HTMLAttributes<HTMLDivElement>): import("react").JSX.Element;
|
|
6
|
+
export declare const Card: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
7
|
+
variant?: CardVariant;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
} & import("react").RefAttributes<HTMLDivElement>> & {
|
|
10
|
+
Header: typeof Header;
|
|
11
|
+
Body: typeof Body;
|
|
12
|
+
Footer: typeof Footer;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
package/dist/Card.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { cx } from './cx';
|
|
4
|
+
const CardRoot = forwardRef(function Card({ variant, className, children, ...props }, ref) {
|
|
5
|
+
return (_jsx("div", { ref: ref, className: cx('card', variant && `card--${variant}`, className), ...props, children: children }));
|
|
6
|
+
});
|
|
7
|
+
function Header({ className, children, ...props }) {
|
|
8
|
+
return _jsx("div", { className: cx('card__header', className), ...props, children: children });
|
|
9
|
+
}
|
|
10
|
+
function Body({ className, children, ...props }) {
|
|
11
|
+
return _jsx("div", { className: cx('card__body', className), ...props, children: children });
|
|
12
|
+
}
|
|
13
|
+
function Footer({ className, children, ...props }) {
|
|
14
|
+
return _jsx("div", { className: cx('card__footer', className), ...props, children: children });
|
|
15
|
+
}
|
|
16
|
+
export const Card = Object.assign(CardRoot, { Header, Body, Footer });
|
package/dist/Chip.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react';
|
|
2
|
+
type ChipProps = HTMLAttributes<HTMLSpanElement> & {
|
|
3
|
+
label: string;
|
|
4
|
+
size?: 'sm' | 'md';
|
|
5
|
+
clickable?: boolean;
|
|
6
|
+
removable?: boolean;
|
|
7
|
+
onRemove?: () => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function Chip({ label, size, clickable, removable, onClick, onRemove, className, ...props }: ChipProps): import("react").JSX.Element;
|
|
10
|
+
export {};
|
package/dist/Chip.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './cx';
|
|
3
|
+
export function Chip({ label, size, clickable, removable, onClick, onRemove, className, ...props }) {
|
|
4
|
+
return (_jsxs("span", { className: cx('chip', size && size !== 'md' && `chip--${size}`, clickable && 'chip--clickable', removable && 'chip--removable', className), onClick: onClick, ...props, children: [label, removable && (_jsx("button", { type: "button", className: "chip__remove", "aria-label": "Remove", onClick: (e) => {
|
|
5
|
+
e.stopPropagation();
|
|
6
|
+
onRemove?.();
|
|
7
|
+
} }))] }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type HTMLAttributes } from 'react';
|
|
2
|
+
type DividerProps = HTMLAttributes<HTMLHRElement> & {
|
|
3
|
+
orientation?: 'horizontal' | 'vertical';
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* A thin rule that separates content. Renders an <hr> (implicit
|
|
7
|
+
* role="separator"); pass orientation="vertical" for use inside a flex row.
|
|
8
|
+
*/
|
|
9
|
+
export declare function Divider({ orientation, className, ...props }: DividerProps): import("react").JSX.Element;
|
|
10
|
+
export {};
|
package/dist/Divider.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './cx';
|
|
3
|
+
/**
|
|
4
|
+
* A thin rule that separates content. Renders an <hr> (implicit
|
|
5
|
+
* role="separator"); pass orientation="vertical" for use inside a flex row.
|
|
6
|
+
*/
|
|
7
|
+
export function Divider({ orientation = 'horizontal', className, ...props }) {
|
|
8
|
+
return (_jsx("hr", { className: cx('divider', orientation === 'vertical' && 'divider--vertical', className), "aria-orientation": orientation, ...props }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A square, icon-only button. Always needs an aria-label because there's no
|
|
4
|
+
* visible text to name it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const IconButton: import("react").ForwardRefExoticComponent<ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
7
|
+
/** Required: describes the action for screen readers, since the button
|
|
8
|
+
* holds only an icon. */
|
|
9
|
+
'aria-label': string;
|
|
10
|
+
size?: 'sm' | 'md';
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
} & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { cx } from './cx';
|
|
4
|
+
/**
|
|
5
|
+
* A square, icon-only button. Always needs an aria-label because there's no
|
|
6
|
+
* visible text to name it.
|
|
7
|
+
*/
|
|
8
|
+
export const IconButton = forwardRef(function IconButton({ size, className, children, type, ...props }, ref) {
|
|
9
|
+
return (_jsx("button", { ref: ref, type: type ?? 'button', className: cx('icon-btn', size && size !== 'md' && `icon-btn--${size}`, className), ...props, children: children }));
|
|
10
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type InfoTipProps = {
|
|
2
|
+
/** The explanatory text shown in the tooltip. */
|
|
3
|
+
content: string;
|
|
4
|
+
side?: 'top' | 'bottom' | 'left' | 'right';
|
|
5
|
+
/** Accessible name for the trigger. Defaults to "More information". */
|
|
6
|
+
label?: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* A small "i" glyph that reveals a tooltip on hover or focus. A convenience
|
|
10
|
+
* wrapper over Tooltip for the very common "explain this label" case.
|
|
11
|
+
*/
|
|
12
|
+
export declare function InfoTip({ content, side, label }: InfoTipProps): import("react").JSX.Element;
|
|
13
|
+
export {};
|
package/dist/InfoTip.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Tooltip } from './Tooltip';
|
|
3
|
+
/**
|
|
4
|
+
* A small "i" glyph that reveals a tooltip on hover or focus. A convenience
|
|
5
|
+
* wrapper over Tooltip for the very common "explain this label" case.
|
|
6
|
+
*/
|
|
7
|
+
export function InfoTip({ content, side = 'top', label = 'More information' }) {
|
|
8
|
+
return (_jsx(Tooltip, { content: content, side: side, children: _jsx("span", { className: "info-tip", role: "img", "aria-label": label, tabIndex: 0, children: "i" }) }));
|
|
9
|
+
}
|
package/dist/Input.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type InputHTMLAttributes } from 'react';
|
|
2
|
+
export declare const Input: import("react").ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "size"> & {
|
|
3
|
+
label?: string;
|
|
4
|
+
error?: string;
|
|
5
|
+
helper?: string;
|
|
6
|
+
size?: 'sm' | 'md';
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
} & import("react").RefAttributes<HTMLInputElement>>;
|
package/dist/Input.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId } from 'react';
|
|
3
|
+
import { cx } from './cx';
|
|
4
|
+
export const Input = forwardRef(function Input({ label, error, helper, size, disabled, className, ...props }, ref) {
|
|
5
|
+
const id = useId();
|
|
6
|
+
const helperId = `${id}-helper`;
|
|
7
|
+
const helperText = error || helper;
|
|
8
|
+
return (_jsxs("div", { className: "input__wrapper", children: [label && _jsx("label", { className: "input__label", htmlFor: id, children: label }), _jsx("input", { ref: ref, id: id, className: cx('input', size && size !== 'md' && `input--${size}`, error && 'input--error', className), disabled: disabled, "aria-invalid": error ? true : undefined, "aria-describedby": helperText ? helperId : undefined, ...props }), helperText && (_jsx("span", { id: helperId, className: cx('input__helper', error && 'input__helper--error'), children: helperText }))] }));
|
|
9
|
+
});
|
package/dist/Modal.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
type ModalProps = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
title?: string;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
declare function Header({ children }: {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
declare function Body({ children }: {
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
declare function Footer({ children }: {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
|
+
export declare function Modal({ open, onClose, title, children }: ModalProps): import("react").ReactPortal | null;
|
|
18
|
+
export declare namespace Modal {
|
|
19
|
+
export { Header };
|
|
20
|
+
export { Body };
|
|
21
|
+
export { Footer };
|
|
22
|
+
}
|
|
23
|
+
export {};
|
package/dist/Modal.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useId } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
function Header({ children }) {
|
|
5
|
+
return _jsx("div", { className: "modal__header", children: children });
|
|
6
|
+
}
|
|
7
|
+
function Body({ children }) {
|
|
8
|
+
return _jsx("div", { className: "modal__body", children: children });
|
|
9
|
+
}
|
|
10
|
+
function Footer({ children }) {
|
|
11
|
+
return _jsx("div", { className: "modal__footer", children: children });
|
|
12
|
+
}
|
|
13
|
+
export function Modal({ open, onClose, title, children }) {
|
|
14
|
+
const titleId = useId();
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!open)
|
|
17
|
+
return;
|
|
18
|
+
function handleKey(e) {
|
|
19
|
+
if (e.key === 'Escape')
|
|
20
|
+
onClose();
|
|
21
|
+
}
|
|
22
|
+
document.addEventListener('keydown', handleKey);
|
|
23
|
+
return () => document.removeEventListener('keydown', handleKey);
|
|
24
|
+
}, [open, onClose]);
|
|
25
|
+
if (!open)
|
|
26
|
+
return null;
|
|
27
|
+
return createPortal(_jsx("div", { className: "modal__backdrop", onClick: onClose, children: _jsxs("div", { role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId : undefined, className: "modal", onClick: (e) => e.stopPropagation(), children: [title && (_jsx("div", { id: titleId, className: "modal__title", children: title })), children] }) }), document.body);
|
|
28
|
+
}
|
|
29
|
+
Modal.Header = Header;
|
|
30
|
+
Modal.Body = Body;
|
|
31
|
+
Modal.Footer = Footer;
|
package/dist/Skeleton.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './cx';
|
|
3
|
+
export function Skeleton({ variant, width, height }) {
|
|
4
|
+
const style = variant === 'rect'
|
|
5
|
+
? {
|
|
6
|
+
'--skeleton-w': width,
|
|
7
|
+
'--skeleton-h': height,
|
|
8
|
+
}
|
|
9
|
+
: undefined;
|
|
10
|
+
return (_jsx("div", { className: cx('skeleton', variant && `skeleton--${variant}`), style: style }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type HTMLAttributes } from 'react';
|
|
2
|
+
type SpinnerProps = HTMLAttributes<HTMLSpanElement> & {
|
|
3
|
+
size?: 'sm' | 'md' | 'lg';
|
|
4
|
+
/** Accessible name announced to screen readers. Defaults to "Loading". */
|
|
5
|
+
label?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* An indeterminate loading spinner. Renders as a live status region so
|
|
9
|
+
* assistive tech announces that something is loading.
|
|
10
|
+
*/
|
|
11
|
+
export declare function Spinner({ size, label, className, ...props }: SpinnerProps): import("react").JSX.Element;
|
|
12
|
+
export {};
|
package/dist/Spinner.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './cx';
|
|
3
|
+
/**
|
|
4
|
+
* An indeterminate loading spinner. Renders as a live status region so
|
|
5
|
+
* assistive tech announces that something is loading.
|
|
6
|
+
*/
|
|
7
|
+
export function Spinner({ size, label = 'Loading', className, ...props }) {
|
|
8
|
+
return (_jsx("span", { role: "status", "aria-label": label, className: cx('spinner', size && size !== 'md' && `spinner--${size}`, className), ...props }));
|
|
9
|
+
}
|
package/dist/Switch.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* An on/off toggle. Controlled via `checked` + `onCheckedChange`. Exposes
|
|
4
|
+
* role="switch" with aria-checked so assistive tech reads its state. Give it
|
|
5
|
+
* an aria-label (or aria-labelledby) since it has no text of its own.
|
|
6
|
+
*/
|
|
7
|
+
export declare const Switch: import("react").ForwardRefExoticComponent<Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onChange"> & {
|
|
8
|
+
/** Whether the switch is on. Controlled. */
|
|
9
|
+
checked: boolean;
|
|
10
|
+
/** Called with the next value when toggled. */
|
|
11
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
} & import("react").RefAttributes<HTMLButtonElement>>;
|
package/dist/Switch.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { cx } from './cx';
|
|
4
|
+
/**
|
|
5
|
+
* An on/off toggle. Controlled via `checked` + `onCheckedChange`. Exposes
|
|
6
|
+
* role="switch" with aria-checked so assistive tech reads its state. Give it
|
|
7
|
+
* an aria-label (or aria-labelledby) since it has no text of its own.
|
|
8
|
+
*/
|
|
9
|
+
export const Switch = forwardRef(function Switch({ checked, onCheckedChange, disabled, className, ...props }, ref) {
|
|
10
|
+
return (_jsx("button", { ref: ref, type: "button", role: "switch", "aria-checked": checked, disabled: disabled, onClick: () => onCheckedChange?.(!checked), className: cx('switch', checked && 'switch--on', className), ...props, children: _jsx("span", { className: "switch__thumb" }) }));
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type TextareaHTMLAttributes } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Multi-line text field. Mirrors Input's label/error/helper API so the two
|
|
4
|
+
* feel the same in a form.
|
|
5
|
+
*/
|
|
6
|
+
export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
|
7
|
+
label?: string;
|
|
8
|
+
error?: string;
|
|
9
|
+
helper?: string;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
} & import("react").RefAttributes<HTMLTextAreaElement>>;
|
package/dist/Textarea.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId } from 'react';
|
|
3
|
+
import { cx } from './cx';
|
|
4
|
+
/**
|
|
5
|
+
* Multi-line text field. Mirrors Input's label/error/helper API so the two
|
|
6
|
+
* feel the same in a form.
|
|
7
|
+
*/
|
|
8
|
+
export const Textarea = forwardRef(function Textarea({ label, error, helper, disabled, className, ...props }, ref) {
|
|
9
|
+
const id = useId();
|
|
10
|
+
const helperId = `${id}-helper`;
|
|
11
|
+
const helperText = error || helper;
|
|
12
|
+
return (_jsxs("div", { className: "input__wrapper", children: [label && (_jsx("label", { className: "input__label", htmlFor: id, children: label })), _jsx("textarea", { ref: ref, id: id, className: cx('textarea', error && 'textarea--error', className), disabled: disabled, "aria-invalid": error ? true : undefined, "aria-describedby": helperText ? helperId : undefined, ...props }), helperText && (_jsx("span", { id: helperId, className: cx('input__helper', error && 'input__helper--error'), children: helperText }))] }));
|
|
13
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
type TooltipProps = {
|
|
3
|
+
content: string;
|
|
4
|
+
side?: 'top' | 'bottom' | 'left' | 'right';
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
};
|
|
7
|
+
export declare function Tooltip({ content, side, children }: TooltipProps): import("react").JSX.Element;
|
|
8
|
+
export {};
|
package/dist/Tooltip.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useId } from 'react';
|
|
3
|
+
import { cx } from './cx';
|
|
4
|
+
export function Tooltip({ content, side = 'top', children }) {
|
|
5
|
+
const [visible, setVisible] = useState(false);
|
|
6
|
+
const id = useId();
|
|
7
|
+
return (_jsxs("span", { onMouseEnter: () => setVisible(true), onMouseLeave: () => setVisible(false), "aria-describedby": id, style: { position: 'relative', display: 'inline-block' }, children: [children, _jsx("span", { id: id, role: "tooltip", className: cx('tooltip', `tooltip--${side}`, visible && 'tooltip--visible'), children: content })] }));
|
|
8
|
+
}
|
package/dist/cx.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cx(...classes: (string | false | null | undefined)[]): string;
|
package/dist/cx.js
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { Button } from './Button';
|
|
2
|
+
export { IconButton } from './IconButton';
|
|
3
|
+
export { Card } from './Card';
|
|
4
|
+
export { Chip } from './Chip';
|
|
5
|
+
export { Input } from './Input';
|
|
6
|
+
export { Textarea } from './Textarea';
|
|
7
|
+
export { Switch } from './Switch';
|
|
8
|
+
export { Modal } from './Modal';
|
|
9
|
+
export { Tooltip } from './Tooltip';
|
|
10
|
+
export { InfoTip } from './InfoTip';
|
|
11
|
+
export { Avatar } from './Avatar';
|
|
12
|
+
export { Badge } from './Badge';
|
|
13
|
+
export { Skeleton } from './Skeleton';
|
|
14
|
+
export { Spinner } from './Spinner';
|
|
15
|
+
export { Divider } from './Divider';
|
|
16
|
+
export { VisuallyHidden } from './VisuallyHidden';
|
|
17
|
+
export { cx } from './cx';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { Button } from './Button';
|
|
2
|
+
export { IconButton } from './IconButton';
|
|
3
|
+
export { Card } from './Card';
|
|
4
|
+
export { Chip } from './Chip';
|
|
5
|
+
export { Input } from './Input';
|
|
6
|
+
export { Textarea } from './Textarea';
|
|
7
|
+
export { Switch } from './Switch';
|
|
8
|
+
export { Modal } from './Modal';
|
|
9
|
+
export { Tooltip } from './Tooltip';
|
|
10
|
+
export { InfoTip } from './InfoTip';
|
|
11
|
+
export { Avatar } from './Avatar';
|
|
12
|
+
export { Badge } from './Badge';
|
|
13
|
+
export { Skeleton } from './Skeleton';
|
|
14
|
+
export { Spinner } from './Spinner';
|
|
15
|
+
export { Divider } from './Divider';
|
|
16
|
+
export { VisuallyHidden } from './VisuallyHidden';
|
|
17
|
+
export { cx } from './cx';
|