@inkorange/space-ui 1.0.3 → 1.1.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 +131 -138
- package/dist/components/AlertDialog.d.ts +17 -8
- package/dist/components/Dialog.d.ts +12 -12
- package/dist/components/DropdownMenu.d.ts +30 -28
- package/dist/components/RadioGroup.d.ts +17 -9
- package/dist/components/Select.d.ts +30 -47
- package/dist/components/Tabs.d.ts +21 -14
- package/dist/components/TextField.d.ts +6 -10
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +842 -889
- package/dist/index.js.map +1 -1
- package/dist/space-ui.css +1 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,204 +1,197 @@
|
|
|
1
|
-
# SpaceUI
|
|
1
|
+
# SpaceUI: Dark interfaces with their own light source
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<!-- badges:start -->
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@inkorange/space-ui)
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
[](https://space-components.vercel.app)
|
|
7
10
|
|
|
8
|
-
<!--
|
|
9
|
-
resolve against the registry rather than the repository. -->
|
|
10
|
-
<img alt="A lit-glass button, select, badges, slider, progress bar and folder tabs on a dark ground"
|
|
11
|
-
src="https://raw.githubusercontent.com/inkorange/space-ui/main/.github/assets/components.svg"
|
|
12
|
-
width="100%">
|
|
13
|
-
|
|
14
|
-
📖 **[Browse the gallery →](https://space-components.vercel.app)** — every
|
|
15
|
-
component, its props, its tokens, and the source for each example.
|
|
11
|
+
<!-- badges:end -->
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
width="100%">
|
|
21
|
-
</a>
|
|
13
|
+
Lit glass. An orbiting rim. Faint starlight across a surface. Not a theme you
|
|
14
|
+
configure — the way these components look out of the box, with nothing to
|
|
15
|
+
compose and nothing to opt into.
|
|
22
16
|
|
|
23
|
-
|
|
17
|
+
<!-- Absolute URLs on purpose: npm renders this file too, and relative paths
|
|
18
|
+
resolve against the registry rather than the repository. This sheet is a
|
|
19
|
+
screenshot of the real components, shot from the gallery — see
|
|
20
|
+
`pnpm assets`. -->
|
|
21
|
+
<img alt="Stickersheet: Button, Badge, Select, TextField, Slider, Progress, IconToggle, RadioGroup, Loader, Tabs, icons, Text, TextArea, Card and ScrollArea on a dark ground"
|
|
22
|
+
src="https://raw.githubusercontent.com/inkorange/space-ui/main/.github/assets/components.jpg"
|
|
23
|
+
width="100%">
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
pnpm add @inkorange/space-ui
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
```tsx
|
|
30
|
-
// once, at your app entry — one import, defaults included
|
|
31
30
|
import "@inkorange/space-ui/styles.css";
|
|
32
|
-
|
|
33
|
-
import { Button, Card, Heading } from "@inkorange/space-ui";
|
|
31
|
+
import { Card, Heading, Text, Badge, Button } from "@inkorange/space-ui";
|
|
34
32
|
|
|
35
33
|
export function Planet() {
|
|
36
34
|
return (
|
|
37
35
|
<Card>
|
|
38
|
-
<Heading size="
|
|
36
|
+
<Heading size="5">Kepler-442b</Heading>
|
|
37
|
+
<Badge color="success">Temperate</Badge>
|
|
38
|
+
<Text size="2" color="muted">
|
|
39
|
+
A super-earth in the habitable zone, 1,206 light years out.
|
|
40
|
+
</Text>
|
|
39
41
|
<Button>Build planet</Button>
|
|
40
42
|
</Card>
|
|
41
43
|
);
|
|
42
44
|
}
|
|
43
45
|
```
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
baked in**, so that single import gives you a working library. Plain CSS — no
|
|
47
|
-
Sass, CSS modules, or framework tooling required downstream. All components are
|
|
48
|
-
client components (`"use client"` is baked into the bundle).
|
|
47
|
+
That is the whole setup. Every component arrives already wearing the design.
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
## The design language is the component
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
Most systems hand you primitives and a theme to paint them with. This one does
|
|
52
|
+
not separate the two. A `Button` is a lit-glass capsule whose rim catches light
|
|
53
|
+
like a planet's atmosphere; a `Slider` is a glass tube with a limb-lit moon you
|
|
54
|
+
drag along it. There is no plain mode, because a plain mode would be a
|
|
55
|
+
different library.
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
:
|
|
58
|
-
--sp-gray-panel: #ffffff;
|
|
59
|
-
--sp-gray-text: #14161c;
|
|
60
|
-
--sp-primary-solid: #7c5cff;
|
|
61
|
-
--sp-font-family: "Inter", system-ui, sans-serif;
|
|
62
|
-
}
|
|
63
|
-
```
|
|
57
|
+
What you *can* control is movement. Ambient motion suits a hero button and
|
|
58
|
+
distracts in a dense form, so one prop stills it — the look untouched, only the
|
|
59
|
+
loops stopped:
|
|
64
60
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
```tsx
|
|
62
|
+
<Button>Build planet</Button>
|
|
63
|
+
{/* rim orbits, stars twinkle */}
|
|
64
|
+
<Button animated={false}>Build planet</Button>
|
|
65
|
+
{/* same glass, held still */}
|
|
66
|
+
```
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
There is no light theme.
|
|
68
|
+
Readers who ask for reduced motion get the stilled treatment automatically,
|
|
69
|
+
whatever the prop says.
|
|
73
70
|
|
|
74
|
-
##
|
|
71
|
+
## Built for dense, dark UI
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
Forms, overlays and controls that hold up when there are a lot of them on
|
|
74
|
+
screen at once — the kind of interface where someone is adjusting values and
|
|
75
|
+
watching something respond.
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
77
|
+
```tsx
|
|
78
|
+
<Select value={star} onValueChange={setStar}>
|
|
79
|
+
{types.map((t) => (
|
|
80
|
+
<Select.Item key={t} value={t}>{t}-type star</Select.Item>
|
|
81
|
+
))}
|
|
82
|
+
</Select>
|
|
83
|
+
```
|
|
86
84
|
|
|
87
|
-
|
|
85
|
+
The small things are handled so you never have to notice them. That Select
|
|
86
|
+
sizes itself to its widest option, so it does not change width as you choose.
|
|
87
|
+
Dialogs ride the browser's own top layer, so focus trapping and Escape are the
|
|
88
|
+
platform's job rather than a library's approximation. Tabs connect to the panel
|
|
89
|
+
beneath them.
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
`gray-11` a label, a border, or a placeholder? SpaceUI ships seven, each named
|
|
91
|
-
for the work it does, so the name tells you where it belongs and a reviewer can
|
|
92
|
-
tell when it is wrong. Set a role's value once and every correct use follows.
|
|
91
|
+
## Make it yours — it's all CSS variables
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
No component holds a value of its own. Every colour, size and duration in the
|
|
94
|
+
library resolves through a **CSS custom property** in the `--sp-` namespace,
|
|
95
|
+
and the defaults ship inside `styles.css` — so retheming is a block of
|
|
96
|
+
variables you write, never a fork, a config file, or a build step.
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
@import "@inkorange/space-ui/tokens.css";
|
|
98
|
+
There are three tiers, from broadest to narrowest.
|
|
99
99
|
|
|
100
|
-
.
|
|
101
|
-
|
|
102
|
-
border: 1px solid var(--sp-gray-border);
|
|
103
|
-
padding: var(--spacing-md); /* 16px — on the grid */
|
|
104
|
-
gap: var(--spacing-sm);
|
|
105
|
-
}
|
|
100
|
+
**Palette roles.** Named for the job the colour does, never the hue. Change one
|
|
101
|
+
and every component playing that role moves with it:
|
|
106
102
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
103
|
+
```css
|
|
104
|
+
:root {
|
|
105
|
+
--sp-primary-solid: #7c5cff; /* solid accent fill: badges, progress */
|
|
106
|
+
--sp-primary-text: #b9a6ff; /* accent text and links */
|
|
107
|
+
--sp-gray-panel: #14161c; /* card and panel fills */
|
|
108
|
+
--sp-gray-border: #d6ebfd30; /* hairlines and control borders */
|
|
109
|
+
--sp-danger-text: #ff9592; /* destructive, not "red" */
|
|
110
110
|
}
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
**Surface channels.** The lit glass is built from layered translucency, so
|
|
114
|
+
those variables carry colour *channels* and let each layer pick its own alpha:
|
|
115
115
|
|
|
116
116
|
```css
|
|
117
|
-
/* theme.css — loaded after tokens.css */
|
|
118
117
|
:root {
|
|
119
|
-
--sp-
|
|
120
|
-
--sp-
|
|
121
|
-
--sp-
|
|
118
|
+
--sp-rim-rgb: 190 160 255; /* the lit atmosphere limb */
|
|
119
|
+
--sp-glow-rgb: 150 130 255; /* hover and focus bloom */
|
|
120
|
+
--sp-glass-rgb: 18 14 34; /* the glass fill itself */
|
|
122
121
|
}
|
|
123
122
|
```
|
|
124
123
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
### Fonts
|
|
124
|
+
Inside the components these read as `rgb(var(--sp-rim-rgb) / 0.4)`, so give
|
|
125
|
+
them space-separated channels — a `#hex` or a full `rgb(...)` leaves the alpha
|
|
126
|
+
with nothing to work on.
|
|
130
127
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
every component. Two components pin a stack (`Badge` and `Button`), and
|
|
134
|
-
both read the same token:
|
|
128
|
+
**Component properties.** Where one component needs a dial of its own it
|
|
129
|
+
exposes one, always named `--sp-[component]-[modifier]-[type]`:
|
|
135
130
|
|
|
136
131
|
```css
|
|
137
|
-
|
|
132
|
+
--sp-progress-height: 12px;
|
|
133
|
+
--sp-select-trigger-max-width: 24rem;
|
|
134
|
+
--sp-loader-moon-size: 10px;
|
|
138
135
|
```
|
|
139
136
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
introduces its own.
|
|
137
|
+
Every component page in the gallery lists its own in a **Custom properties**
|
|
138
|
+
table, saying what each one changes.
|
|
143
139
|
|
|
144
|
-
|
|
140
|
+
Because these are ordinary CSS variables, they cascade. Declare them on `:root`
|
|
141
|
+
to theme the application, or on any element to retheme just that subtree —
|
|
142
|
+
a warmer palette inside one panel, a tighter control in one toolbar:
|
|
145
143
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
```tsx
|
|
154
|
-
<Button>Build planet</Button> {/* ambient motion, the default */}
|
|
155
|
-
<Button animated={false}>Build planet</Button> {/* same look, no loops */}
|
|
156
|
-
|
|
157
|
-
<TextField.Root animated={false} />
|
|
158
|
-
<Select.Trigger animated={false} />
|
|
144
|
+
```css
|
|
145
|
+
.launch-panel {
|
|
146
|
+
--sp-glass-rgb: 30 16 14;
|
|
147
|
+
--sp-rim-rgb: 255 180 130;
|
|
148
|
+
--sp-control-height: 32px;
|
|
149
|
+
}
|
|
159
150
|
```
|
|
160
151
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
whatever the prop says.
|
|
165
|
-
|
|
166
|
-
`Slider` takes no `animated` prop: its skin has no ambient loop, and a prop
|
|
167
|
-
that does nothing is worse than no prop.
|
|
152
|
+
You override only what you name, and everything you leave alone keeps its
|
|
153
|
+
shipped value. Supply no variables at all and the library still renders exactly
|
|
154
|
+
as it does in the gallery.
|
|
168
155
|
|
|
169
|
-
##
|
|
156
|
+
## The components
|
|
170
157
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
the spacing grid survive where parity with the shipped app beat grid purity,
|
|
175
|
-
and they are commented where they occur.
|
|
158
|
+
Every one of these has a live page in the gallery — the component running, the
|
|
159
|
+
source behind it, its full prop table with defaults, and the custom properties
|
|
160
|
+
it exposes.
|
|
176
161
|
|
|
162
|
+
| | |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| **Typography** | [Text](https://space-components.vercel.app/?story=components--typography--text-sizes) · [Link](https://space-components.vercel.app/?story=components--typography--text-sizes) · [Heading](https://space-components.vercel.app/?story=components--typography--headings) · [Badge](https://space-components.vercel.app/?story=components--typography--badges) · [Separator](https://space-components.vercel.app/?story=components--typography--separator) |
|
|
165
|
+
| **Layout** | [Card](https://space-components.vercel.app/?story=components--layout--card) · [Flex](https://space-components.vercel.app/?story=components--layout--flex-and-grid) · [Grid](https://space-components.vercel.app/?story=components--layout--flex-and-grid) · [Box](https://space-components.vercel.app/?story=components--layout--flex-and-grid) · [ScrollArea](https://space-components.vercel.app/?story=components--layout--scroll-area) |
|
|
166
|
+
| **Forms** | [TextField](https://space-components.vercel.app/?story=components--forms--text-field) · [TextArea](https://space-components.vercel.app/?story=components--forms--text-area) · [Select](https://space-components.vercel.app/?story=components--forms--select) · [Slider](https://space-components.vercel.app/?story=components--forms--slider) · [RadioGroup](https://space-components.vercel.app/?story=components--forms--radio-group) |
|
|
167
|
+
| **Buttons** | [Button](https://space-components.vercel.app/?story=components--buttons--buttons) · [IconToggle](https://space-components.vercel.app/?story=components--buttons--icon-toggle) |
|
|
168
|
+
| **Feedback** | [Loader](https://space-components.vercel.app/?story=components--buttons--loader) · [Progress](https://space-components.vercel.app/?story=components--buttons--progress) |
|
|
169
|
+
| **Overlays** | [Dialog](https://space-components.vercel.app/?story=components--overlays--dialog) · [AlertDialog](https://space-components.vercel.app/?story=components--overlays--alert-dialog) · [DropdownMenu](https://space-components.vercel.app/?story=components--overlays--dropdown-menu) · [Tabs](https://space-components.vercel.app/?story=components--overlays--tabs) · [Tooltip](https://space-components.vercel.app/?story=components--overlays--tooltip) |
|
|
170
|
+
| **Icons** | [20 icons](https://space-components.vercel.app/?story=components--icons--all-icons) |
|
|
171
|
+
| **Foundations** | [Color](https://space-components.vercel.app/?story=foundations--color) · [Spacing](https://space-components.vercel.app/?story=foundations--spacing) · [Type scale](https://space-components.vercel.app/?story=foundations--type-scale) · [Motion](https://space-components.vercel.app/?story=foundations--motion) · [Token usage](https://space-components.vercel.app/?story=foundations--token-usage) |
|
|
177
172
|
|
|
178
|
-
|
|
173
|
+
<a href="https://space-components.vercel.app">
|
|
174
|
+
<img alt="The SpaceUI documentation gallery: components, their props, tokens and source"
|
|
175
|
+
src="https://raw.githubusercontent.com/inkorange/space-ui/main/.github/assets/doc-screen.jpg"
|
|
176
|
+
width="100%">
|
|
177
|
+
</a>
|
|
179
178
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
pnpm test # 49 tests, incl. the Radix-ban and token-grid guards
|
|
184
|
-
pnpm build
|
|
185
|
-
```
|
|
179
|
+
Start at **[space-components.vercel.app](https://space-components.vercel.app)** — the
|
|
180
|
+
introduction covers the token system and how to theme it, and every component
|
|
181
|
+
page shows the code you would actually write.
|
|
186
182
|
|
|
187
|
-
|
|
188
|
-
hot-reload with no build step in the loop. Prop tables and token usage on the
|
|
189
|
-
docs site are generated from the source at build time, so they cannot drift
|
|
190
|
-
from what ships.
|
|
183
|
+
## What you get
|
|
191
184
|
|
|
192
|
-
|
|
185
|
+
**Zero runtime dependencies**, and about 22 kB over the wire gzipped — a 16 kB
|
|
186
|
+
ESM bundle and a 7 kB stylesheet. React 19, precompiled CSS, no build tooling
|
|
187
|
+
required downstream. Both numbers are measured from the build on every CI run,
|
|
188
|
+
so the badges above cannot drift.
|
|
193
189
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
opens/updates a "Version Packages" PR; merging that publishes to npm and tags
|
|
197
|
-
the release.
|
|
190
|
+
These ran in production before they became a package, so they arrived with
|
|
191
|
+
their edge cases already found rather than imagined.
|
|
198
192
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
- **major** — removed/renamed export or prop, changed rendering default
|
|
193
|
+
**Requires** React 19 and a dark surface. There is deliberately no light theme;
|
|
194
|
+
these components assume the dark they were drawn for.
|
|
202
195
|
|
|
203
196
|
## License
|
|
204
197
|
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
import { Trigger
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { Trigger, Close, Content, Title, Description, type DialogProps } from "./Dialog";
|
|
2
|
+
export type { DialogProps as AlertDialogProps };
|
|
3
|
+
/**
|
|
4
|
+
* A modal that interrupts to confirm something consequential.
|
|
5
|
+
*
|
|
6
|
+
* Unlike a Dialog it has no backdrop dismissal, because dismissing an alert
|
|
7
|
+
* by missing it is not a decision. Put `Cancel` before `Action` so the safe
|
|
8
|
+
* choice takes focus when it opens.
|
|
9
|
+
*/
|
|
10
|
+
export declare const AlertDialog: (({ open, onOpenChange, children }: DialogProps) => import("react").JSX.Element) & {
|
|
11
|
+
Trigger: typeof Trigger;
|
|
12
|
+
Content: typeof Content;
|
|
13
|
+
Title: typeof Title;
|
|
14
|
+
Description: typeof Description;
|
|
15
|
+
Cancel: typeof Close;
|
|
16
|
+
Action: typeof Close;
|
|
17
|
+
};
|
|
@@ -14,26 +14,19 @@ export interface DialogCtx {
|
|
|
14
14
|
setHasDesc: (v: boolean) => void;
|
|
15
15
|
}
|
|
16
16
|
export declare const useDialog: () => DialogCtx;
|
|
17
|
-
export interface
|
|
17
|
+
export interface DialogProps {
|
|
18
18
|
/** Controls the dialog. Omit both this and onOpenChange to let the dialog
|
|
19
19
|
* manage its own state. */
|
|
20
20
|
open?: boolean;
|
|
21
21
|
/** Called whenever the dialog opens or closes, including via Escape or a
|
|
22
22
|
* backdrop click. */
|
|
23
23
|
onOpenChange?: (o: boolean) => void;
|
|
24
|
-
/** A Trigger and a Content
|
|
24
|
+
/** A `Trigger` and a `Content`, in any arrangement — this renders
|
|
25
|
+
* nothing itself, so they can sit anywhere beneath it. */
|
|
25
26
|
children?: ReactNode;
|
|
26
27
|
}
|
|
27
|
-
/** Shared by Dialog
|
|
28
|
-
export declare function makeRoot(alert: boolean): ({ open, onOpenChange, children }:
|
|
29
|
-
/**
|
|
30
|
-
* Owns a dialog's open state. Works controlled via `open`/`onOpenChange`, or
|
|
31
|
-
* uncontrolled if you pass neither.
|
|
32
|
-
*
|
|
33
|
-
* Renders nothing itself — it only provides context, so Trigger and Content
|
|
34
|
-
* can sit anywhere beneath it in the tree.
|
|
35
|
-
*/
|
|
36
|
-
export declare const Root: ({ open, onOpenChange, children }: RootProps) => React.JSX.Element;
|
|
28
|
+
/** Shared by Dialog and AlertDialog; only the alert flag differs. */
|
|
29
|
+
export declare function makeRoot(alert: boolean): ({ open, onOpenChange, children }: DialogProps) => React.JSX.Element;
|
|
37
30
|
/**
|
|
38
31
|
* Opens the dialog. Chains onto its child's existing click handler rather
|
|
39
32
|
* than replacing it, and renders no element of its own — so the child stays
|
|
@@ -79,3 +72,10 @@ export declare function Title({ className, ...rest }: HeadingProps): React.JSX.E
|
|
|
79
72
|
* description. Optional, and only announced when present.
|
|
80
73
|
*/
|
|
81
74
|
export declare function Description(props: TextProps): React.JSX.Element;
|
|
75
|
+
export declare const Dialog: (({ open, onOpenChange, children }: DialogProps) => React.JSX.Element) & {
|
|
76
|
+
Trigger: typeof Trigger;
|
|
77
|
+
Close: typeof Close;
|
|
78
|
+
Content: typeof Content;
|
|
79
|
+
Title: typeof Title;
|
|
80
|
+
Description: typeof Description;
|
|
81
|
+
};
|
|
@@ -1,42 +1,38 @@
|
|
|
1
1
|
import type * as React from "react";
|
|
2
2
|
import { type ReactNode } from "react";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/** A Trigger and a Content. */
|
|
9
|
-
children?: ReactNode;
|
|
10
|
-
}): React.JSX.Element;
|
|
11
|
-
/**
|
|
12
|
-
* The control that opens the menu, and the element the menu is positioned
|
|
13
|
-
* against. Wraps its child rather than rendering a button of its own.
|
|
14
|
-
*/
|
|
15
|
-
export declare function Trigger({ children, }: {
|
|
16
|
-
/** The control that opens the menu, and what the menu anchors to. */
|
|
17
|
-
children?: ReactNode;
|
|
18
|
-
}): React.ReactElement<Record<string, unknown>, string | React.JSXElementConstructor<any>> | null;
|
|
19
|
-
/**
|
|
20
|
-
* The menu surface, anchored to the trigger and aligned to its start or end
|
|
21
|
-
* edge. Owns the keyboard model: arrows move between items, Escape closes and
|
|
22
|
-
* returns focus to the trigger.
|
|
23
|
-
*/
|
|
24
|
-
export declare function Content({ align, children, }: {
|
|
3
|
+
import { type ButtonProps } from "./Button";
|
|
4
|
+
export interface DropdownMenuProps extends Omit<ButtonProps, "children"> {
|
|
5
|
+
/** The trigger's content. The trigger is always a Button, so this is what
|
|
6
|
+
* goes inside it — text, or an icon with `iconOnly`. */
|
|
7
|
+
label: ReactNode;
|
|
25
8
|
/** Which edge of the trigger the menu lines up with. Use `end` when the
|
|
26
9
|
* trigger sits near the right edge and the menu would overflow. */
|
|
27
10
|
align?: "start" | "end";
|
|
28
|
-
/**
|
|
11
|
+
/** `DropdownMenu.Item`, `.Separator` and `.Label`, in any order. */
|
|
29
12
|
children?: ReactNode;
|
|
30
|
-
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A menu of actions hung off a button.
|
|
16
|
+
*
|
|
17
|
+
* The trigger is built in and is always a Button — a dropdown whose trigger
|
|
18
|
+
* looked like anything else was never a shape worth supporting, and making it
|
|
19
|
+
* arbitrary cost a `cloneElement` that had to graft aria wiring and a click
|
|
20
|
+
* handler onto someone else's element.
|
|
21
|
+
*
|
|
22
|
+
* The menu rides the platform's top layer via `popover`, so click-outside
|
|
23
|
+
* dismissal and stacking come from the browser rather than an approximation.
|
|
24
|
+
* Arrows move between items, Escape closes and returns focus to the trigger.
|
|
25
|
+
*/
|
|
26
|
+
declare function DropdownMenuRoot({ label, align, children, ...buttonProps }: DropdownMenuProps): React.JSX.Element;
|
|
31
27
|
/**
|
|
32
28
|
* A non-interactive heading for a group of items. Skipped by arrow-key
|
|
33
29
|
* navigation, since there is nothing to activate.
|
|
34
30
|
*/
|
|
35
|
-
|
|
31
|
+
declare function Label({ children, }: {
|
|
36
32
|
/** Heading text for the group beneath it. Not focusable. */
|
|
37
33
|
children?: ReactNode;
|
|
38
34
|
}): React.JSX.Element;
|
|
39
|
-
export interface
|
|
35
|
+
export interface DropdownMenuItemProps {
|
|
40
36
|
/** Render the child element instead of the default item — for a link that
|
|
41
37
|
* should stay an anchor rather than becoming a button. */
|
|
42
38
|
asChild?: boolean;
|
|
@@ -54,9 +50,15 @@ export interface ItemProps {
|
|
|
54
50
|
* Pass `color="danger"` for a destructive action so it reads differently from
|
|
55
51
|
* its neighbours before it is chosen, not after.
|
|
56
52
|
*/
|
|
57
|
-
|
|
53
|
+
declare function Item({ asChild, color, onSelect, children }: DropdownMenuItemProps): React.JSX.Element;
|
|
58
54
|
/**
|
|
59
55
|
* A rule between groups of items — for separating a destructive action from
|
|
60
56
|
* routine ones, so the two are not adjacent to a moving cursor.
|
|
61
57
|
*/
|
|
62
|
-
|
|
58
|
+
declare function Separator(): React.JSX.Element;
|
|
59
|
+
export declare const DropdownMenu: typeof DropdownMenuRoot & {
|
|
60
|
+
Item: typeof Item;
|
|
61
|
+
Separator: typeof Separator;
|
|
62
|
+
Label: typeof Label;
|
|
63
|
+
};
|
|
64
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as React from "react";
|
|
2
2
|
import { type ReactNode } from "react";
|
|
3
|
-
export interface
|
|
3
|
+
export interface RadioGroupProps {
|
|
4
4
|
/** The selected item's value. Fully controlled. */
|
|
5
5
|
value: string;
|
|
6
6
|
/** Called with the newly selected value. */
|
|
@@ -9,15 +9,15 @@ export interface RootProps {
|
|
|
9
9
|
disabled?: boolean;
|
|
10
10
|
/** Merged onto the group wrapper. */
|
|
11
11
|
className?: string;
|
|
12
|
-
/**
|
|
12
|
+
/** One or more `RadioGroup.Item`. */
|
|
13
13
|
children?: ReactNode;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* A single-choice control for short lists, where seeing every option at once
|
|
17
17
|
* matters more than saving space. Reach for Select when it does not.
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
|
-
export interface
|
|
19
|
+
declare function RadioGroupRoot({ value, onValueChange, disabled, className, children }: RadioGroupProps): React.JSX.Element;
|
|
20
|
+
export interface RadioGroupItemProps {
|
|
21
21
|
/** Reported to the group's onValueChange when chosen. */
|
|
22
22
|
value: string;
|
|
23
23
|
/** Disables this option only. */
|
|
@@ -25,8 +25,16 @@ export interface ItemProps {
|
|
|
25
25
|
/** The option's label. Part of the click target, not merely beside it. */
|
|
26
26
|
children?: ReactNode;
|
|
27
27
|
}
|
|
28
|
-
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
|
|
28
|
+
/**
|
|
29
|
+
* One option.
|
|
30
|
+
*
|
|
31
|
+
* With children it labels itself: a <label> wraps the input, the orb and the
|
|
32
|
+
* children, so the text is part of the click target. Without children it
|
|
33
|
+
* emits a bare span instead, so an OUTER label can own the association —
|
|
34
|
+
* nesting one label inside another is invalid HTML.
|
|
35
|
+
*/
|
|
36
|
+
declare function Item({ value, disabled, children }: RadioGroupItemProps): React.JSX.Element;
|
|
37
|
+
export declare const RadioGroup: typeof RadioGroupRoot & {
|
|
38
|
+
Item: typeof Item;
|
|
39
|
+
};
|
|
40
|
+
export {};
|