neba 1.0.0 → 1.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 +27 -16
- package/dist/components/avatar/Avatar.d.ts +100 -0
- package/dist/components/avatar/Avatar.js +1 -0
- package/dist/components/avatar/index.d.ts +2 -0
- package/dist/components/avatar/index.js +1 -0
- package/dist/components/blockquote/Blockquote.d.ts +65 -0
- package/dist/components/blockquote/Blockquote.js +1 -0
- package/dist/components/blockquote/index.d.ts +2 -0
- package/dist/components/blockquote/index.js +1 -0
- package/dist/components/breadcrumb/Breadcrumb.d.ts +92 -0
- package/dist/components/breadcrumb/Breadcrumb.js +1 -0
- package/dist/components/breadcrumb/index.d.ts +2 -0
- package/dist/components/breadcrumb/index.js +1 -0
- package/dist/components/carousel/Carousel.d.ts +74 -0
- package/dist/components/carousel/Carousel.js +1 -0
- package/dist/components/carousel/index.d.ts +2 -0
- package/dist/components/carousel/index.js +1 -0
- package/dist/components/chat-bubble/ChatBubble.d.ts +114 -0
- package/dist/components/chat-bubble/ChatBubble.js +1 -0
- package/dist/components/chat-bubble/index.d.ts +2 -0
- package/dist/components/chat-bubble/index.js +1 -0
- package/dist/components/checkbox/Checkbox.js +1 -1
- package/dist/components/combobox/Combobox.js +1 -1
- package/dist/components/container/Container.d.ts +55 -0
- package/dist/components/container/Container.js +1 -0
- package/dist/components/container/index.d.ts +2 -0
- package/dist/components/container/index.js +1 -0
- package/dist/components/date-picker/DatePicker.d.ts +75 -0
- package/dist/components/date-picker/DatePicker.js +1 -0
- package/dist/components/date-picker/index.d.ts +2 -0
- package/dist/components/date-picker/index.js +1 -0
- package/dist/components/date-range-picker/DateRangePicker.d.ts +81 -0
- package/dist/components/date-range-picker/DateRangePicker.js +1 -0
- package/dist/components/date-range-picker/index.d.ts +2 -0
- package/dist/components/date-range-picker/index.js +1 -0
- package/dist/components/date-time-picker/DateTimePicker.d.ts +68 -0
- package/dist/components/date-time-picker/DateTimePicker.js +1 -0
- package/dist/components/date-time-picker/index.d.ts +2 -0
- package/dist/components/date-time-picker/index.js +1 -0
- package/dist/components/dialog/Dialog.js +1 -1
- package/dist/components/divider/Divider.d.ts +19 -0
- package/dist/components/divider/Divider.js +1 -1
- package/dist/components/grid/Grid.d.ts +50 -0
- package/dist/components/grid/Grid.js +1 -0
- package/dist/components/grid/index.d.ts +2 -0
- package/dist/components/grid/index.js +1 -0
- package/dist/components/grid-container/GridContainer.d.ts +79 -0
- package/dist/components/grid-container/GridContainer.js +1 -0
- package/dist/components/grid-container/index.d.ts +2 -0
- package/dist/components/grid-container/index.js +1 -0
- package/dist/components/highlight/Highlight.d.ts +84 -0
- package/dist/components/highlight/Highlight.js +1 -0
- package/dist/components/highlight/index.d.ts +2 -0
- package/dist/components/highlight/index.js +1 -0
- package/dist/components/icon/Icon.d.ts +58 -0
- package/dist/components/icon/Icon.js +1 -0
- package/dist/components/icon/index.d.ts +2 -0
- package/dist/components/icon/index.js +1 -0
- package/dist/components/icon-button/IconButton.d.ts +39 -0
- package/dist/components/icon-button/IconButton.js +1 -0
- package/dist/components/icon-button/index.d.ts +2 -0
- package/dist/components/icon-button/index.js +1 -0
- package/dist/components/otp-field/OtpField.d.ts +85 -0
- package/dist/components/otp-field/OtpField.js +1 -0
- package/dist/components/otp-field/index.d.ts +2 -0
- package/dist/components/otp-field/index.js +1 -0
- package/dist/components/pagination/Pagination.js +1 -1
- package/dist/components/panes/Panes.d.ts +78 -0
- package/dist/components/panes/Panes.js +1 -0
- package/dist/components/panes/index.d.ts +2 -0
- package/dist/components/panes/index.js +1 -0
- package/dist/components/pill/Pill.d.ts +80 -0
- package/dist/components/pill/Pill.js +1 -0
- package/dist/components/pill/index.d.ts +2 -0
- package/dist/components/pill/index.js +1 -0
- package/dist/components/radio-group/RadioGroup.js +1 -1
- package/dist/components/segmented-button/SegmentedButton.d.ts +77 -0
- package/dist/components/segmented-button/SegmentedButton.js +1 -0
- package/dist/components/segmented-button/index.d.ts +2 -0
- package/dist/components/segmented-button/index.js +1 -0
- package/dist/components/select/Select.js +1 -1
- package/dist/components/shortcut/Shortcut.d.ts +60 -0
- package/dist/components/shortcut/Shortcut.js +1 -0
- package/dist/components/shortcut/index.d.ts +2 -0
- package/dist/components/shortcut/index.js +1 -0
- package/dist/components/spoiler/Spoiler.d.ts +95 -0
- package/dist/components/spoiler/Spoiler.js +1 -0
- package/dist/components/spoiler/index.d.ts +2 -0
- package/dist/components/spoiler/index.js +1 -0
- package/dist/components/statistic/Statistic.d.ts +91 -0
- package/dist/components/statistic/Statistic.js +1 -0
- package/dist/components/statistic/index.d.ts +2 -0
- package/dist/components/statistic/index.js +1 -0
- package/dist/components/switch/Switch.js +1 -1
- package/dist/components/text-link/TextLink.d.ts +82 -0
- package/dist/components/text-link/TextLink.js +1 -0
- package/dist/components/text-link/index.d.ts +2 -0
- package/dist/components/text-link/index.js +1 -0
- package/dist/components/time-picker/TimePicker.d.ts +76 -0
- package/dist/components/time-picker/TimePicker.js +1 -0
- package/dist/components/time-picker/index.d.ts +2 -0
- package/dist/components/time-picker/index.js +1 -0
- package/dist/components/timeline/Timeline.d.ts +98 -0
- package/dist/components/timeline/Timeline.js +1 -0
- package/dist/components/timeline/index.d.ts +2 -0
- package/dist/components/timeline/index.js +1 -0
- package/dist/components/toolbar/Toolbar.d.ts +67 -0
- package/dist/components/toolbar/Toolbar.js +1 -0
- package/dist/components/toolbar/index.d.ts +2 -0
- package/dist/components/toolbar/index.js +1 -0
- package/dist/components/tree-view/TreeView.d.ts +115 -0
- package/dist/components/tree-view/TreeView.js +1 -0
- package/dist/components/tree-view/index.d.ts +2 -0
- package/dist/components/tree-view/index.js +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +1 -1
- package/dist/internal/calendar.d.ts +149 -0
- package/dist/internal/calendar.js +1 -0
- package/dist/internal/date.d.ts +170 -0
- package/dist/internal/date.js +1 -0
- package/dist/internal/grid.d.ts +69 -0
- package/dist/internal/grid.js +1 -0
- package/dist/internal/i18n.d.ts +78 -0
- package/dist/internal/i18n.js +1 -0
- package/dist/internal/icons.d.ts +54 -0
- package/dist/internal/icons.js +1 -1
- package/dist/internal/picker.d.ts +111 -0
- package/dist/internal/picker.js +1 -0
- package/dist/internal/sizer.d.ts +33 -0
- package/dist/internal/sizer.js +1 -0
- package/dist/internal/styles.d.ts +33 -0
- package/dist/internal/styles.js +1 -1
- package/dist/styles.css +2 -758
- package/dist/tailwind.css +1017 -0
- package/dist/types.d.ts +65 -0
- package/package.json +25 -12
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
<img src=".github/128x128.png" alt="Neba UI" width="128" height="128" />
|
|
2
|
+
|
|
1
3
|
# Neba UI
|
|
2
4
|
|
|
3
5
|
[](https://github.com/jooy2/neba/blob/main/LICENSE)   [](https://www.npmjs.com/package/neba) [](https://www.npmjs.com/package/neba)  [](https://github.com/jooy2) 
|
|
4
6
|
|
|
5
7
|
### 📘 [**neba.cdget.com**](https://neba.cdget.com)
|
|
6
8
|
|
|
7
|
-
[](https://neba.cdget.com/
|
|
9
|
+
[](https://neba.cdget.com/examples/overview)
|
|
8
10
|
|
|
9
11
|
Live previews and full props for every component. This README is just the quick start.
|
|
10
12
|
|
|
@@ -12,7 +14,7 @@ Live previews and full props for every component. This README is just the quick
|
|
|
12
14
|
|
|
13
15
|
**Neba UI is a React component library for building application interfaces.** It gives you the pieces a real product needs — buttons and form fields, menus and dialogs, tables and tabs, progress and notifications — already styled, already accessible, and already agreeing with each other.
|
|
14
16
|
|
|
15
|
-
You install one package, add
|
|
17
|
+
You install one package, add one line to your CSS, and import components. There is nothing to configure, no theme object to assemble, and no per-component styling decisions to make before you can ship a screen.
|
|
16
18
|
|
|
17
19
|
- **A broad component set, still growing** — enough to build a whole screen without reaching elsewhere.
|
|
18
20
|
- **One shared vocabulary** — `size`, `color`, `variant`, `density`, `elevation`. An `md` is the same height on every control; `primary` is the same colour everywhere.
|
|
@@ -20,7 +22,7 @@ You install one package, add two lines to your CSS, and import components. There
|
|
|
20
22
|
- **Dark mode with no work** — follows the system, and can be forced either way per subtree.
|
|
21
23
|
- **A design language, not a theme file** — a translucent acrylic surface with a hairline edge, one deliberate motion signature, and shadows that are opt-in.
|
|
22
24
|
- **ESM only**, TypeScript declarations included, tree-shakeable — every component compiles to its own module.
|
|
23
|
-
- **One runtime dependency.** React 19, Node.js 18 or later.
|
|
25
|
+
- **One runtime dependency.** React 18 or 19, Node.js 18 or later.
|
|
24
26
|
|
|
25
27
|
## Documentation
|
|
26
28
|
|
|
@@ -30,10 +32,11 @@ Everything is documented at **[neba.cdget.com](https://neba.cdget.com)**, where
|
|
|
30
32
|
| --- | --- |
|
|
31
33
|
| [**Getting started**](https://neba.cdget.com/guide/getting-started) | Install and setup, end to end. |
|
|
32
34
|
| [**All components**](https://neba.cdget.com/components/) | Every component, one page each: live previews and the full props table. |
|
|
33
|
-
| [**Examples**](https://neba.cdget.com/examples/) | A whole sample screen
|
|
34
|
-
| [**Design language**](https://neba.cdget.com/
|
|
35
|
-
| [**Prop conventions**](https://neba.cdget.com/
|
|
36
|
-
| [**Color**](https://neba.cdget.com/
|
|
35
|
+
| [**Examples**](https://neba.cdget.com/examples/overview) | A whole sample screen explained block by block, plus three concept screens — a landing page, an admin dashboard and a sign-up flow. |
|
|
36
|
+
| [**Design language**](https://neba.cdget.com/design/design-language) | Why a Neba surface looks and behaves the way it does. |
|
|
37
|
+
| [**Prop conventions**](https://neba.cdget.com/design/prop-conventions) | The shared vocabulary every component draws from. |
|
|
38
|
+
| [**Color**](https://neba.cdget.com/design/color) | The token families, and how to theme them. |
|
|
39
|
+
| [**Changelog**](https://neba.cdget.com/changelog) | What changed in each release, and the setup changes worth acting on. |
|
|
37
40
|
|
|
38
41
|
Also available in Korean / 한국어 문서: **[neba.cdget.com/ko/](https://neba.cdget.com/ko/)**
|
|
39
42
|
|
|
@@ -47,21 +50,27 @@ npm install neba
|
|
|
47
50
|
pnpm add neba
|
|
48
51
|
```
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
`react` and `react-dom` are peer dependencies — React 18 or 19. Neba uses the copy your project already has, and npm 7 and later will install them alongside it if it has none.
|
|
51
54
|
|
|
52
55
|
### Setup
|
|
53
56
|
|
|
54
|
-
Add
|
|
57
|
+
Add one line to your app's CSS entry point:
|
|
55
58
|
|
|
56
59
|
```css
|
|
57
|
-
@import 'tailwindcss';
|
|
58
60
|
@import 'neba/styles.css';
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
`neba/styles.css`
|
|
63
|
+
`neba/styles.css` is finished CSS — the design tokens, the compiled rules for every utility class the components use, and a small reset whose every rule is specificity 0 so your own styles always win. [Tailwind CSS](https://tailwindcss.com) v4 builds this package; it does not have to be installed in yours.
|
|
62
64
|
|
|
63
65
|
That is the whole setup. No provider is required at the root, no theme object, no config file.
|
|
64
66
|
|
|
67
|
+
If your project already runs Tailwind v4, import `neba/tailwind.css` instead — the token sheet, which registers the package as a style source so your own build generates the utilities in the same pass as your app's:
|
|
68
|
+
|
|
69
|
+
```css
|
|
70
|
+
@import 'tailwindcss';
|
|
71
|
+
@import 'neba/tailwind.css';
|
|
72
|
+
```
|
|
73
|
+
|
|
65
74
|
## Usage
|
|
66
75
|
|
|
67
76
|
Import components from the package root:
|
|
@@ -93,7 +102,7 @@ The reason a Neba screen looks composed rather than assembled is that the props
|
|
|
93
102
|
| `density` | `default` `compact` | Padding only — never the height, never the type scale, so a compact control still lines up with a default one. |
|
|
94
103
|
| `elevation` | `0` `1` `2` `3` | How far a surface floats off the page. `0` is the default and means no shadow at all. |
|
|
95
104
|
|
|
96
|
-
Placement props are logical, not physical — `start`/`end` rather than `left`/`right` — so layouts flip correctly under RTL. The full rules are in [**Prop conventions**](https://neba.cdget.com/
|
|
105
|
+
Placement props are logical, not physical — `start`/`end` rather than `left`/`right` — so layouts flip correctly under RTL. The full rules are in [**Prop conventions**](https://neba.cdget.com/design/prop-conventions).
|
|
97
106
|
|
|
98
107
|
```tsx
|
|
99
108
|
<Button size="sm" color="danger" variant="outline">Delete</Button>
|
|
@@ -102,15 +111,17 @@ Placement props are logical, not physical — `start`/`end` rather than `left`/`
|
|
|
102
111
|
|
|
103
112
|
### Components
|
|
104
113
|
|
|
105
|
-
**Inputs** — Button, ButtonGroup, TextField, NumberField, Select, Combobox, Checkbox, RadioGroup, Switch, Slider, Menu (with submenus, checkbox and radio items), ContextMenu, FilePicker, Pagination
|
|
114
|
+
**Inputs** — Button, IconButton, ButtonGroup, SegmentedButton, TextField, NumberField, OtpField, Select, Combobox, Checkbox, RadioGroup, Switch, Slider, Menu (with submenus, checkbox and radio items), ContextMenu, FilePicker, Pagination, DatePicker, TimePicker, DateTimePicker, DateRangePicker
|
|
106
115
|
|
|
107
|
-
**Surfaces** — Box, Card, Accordion, Tabs
|
|
116
|
+
**Surfaces** — Box, Card, Accordion, Tabs, Carousel, Toolbar, Pill, Spoiler, ChatBubble
|
|
108
117
|
|
|
109
|
-
**Display** — Typography, Divider, Chip, Badge, List, Table
|
|
118
|
+
**Display** — Typography, TextLink, Blockquote, Highlight, Divider, Chip, Badge, Avatar, Icon, Shortcut, Statistic, List, Table, Timeline, Breadcrumb, TreeView
|
|
110
119
|
|
|
111
120
|
**Feedback** — Alert, Dialog, Toast, Tooltip, Overlay, ProgressLinear, ProgressCircular, ProgressBox
|
|
112
121
|
|
|
113
|
-
|
|
122
|
+
**Layout** — Container, Grid (with GridContainer), Panes
|
|
123
|
+
|
|
124
|
+
Recent additions: **ChatBubble** (one message in a conversation, where the avatar, the time, the delivery mark, the media and the link card are each drawn only when given something), **Spoiler** (content covered by a blur rather than hidden, so a reader can see there is something there without reading it by accident), **TextLink** (a link with no surface of its own, and a mark for the ones that take over the window), **Avatar** (falls back to initials, a glyph or a silhouette, so it is never an empty box), **TreeView**, **Breadcrumb**, **Panes** (regions with a draggable bar between each pair) and **OtpField**; before those, **Blockquote**, **Shortcut**, **Highlight**, **SegmentedButton** and **Timeline**, and the four pickers — **DatePicker**, **TimePicker**, **DateTimePicker** and **DateRangePicker** — which take and return a plain `Date` and add no date library to your bundle.
|
|
114
125
|
|
|
115
126
|
Each one has its own page — live previews, every prop, and the variations worth seeing — under [**All components**](https://neba.cdget.com/components/).
|
|
116
127
|
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaColor, NebaElevation, NebaSize, NebaVariant } from '../../types';
|
|
3
|
+
/** What Base UI reports about the picture as it loads. */
|
|
4
|
+
export type AvatarLoadingStatus = 'idle' | 'loading' | 'loaded' | 'error';
|
|
5
|
+
/**
|
|
6
|
+
* The crop, not the sheet.
|
|
7
|
+
*
|
|
8
|
+
* `circle` is the default, and it is the one place besides Badge where the
|
|
9
|
+
* library draws something without a flat run along its edge. The reasoning is
|
|
10
|
+
* Badge's: the cut acrylic edge is what says "this is a surface", and an avatar
|
|
11
|
+
* is not a surface — it is a portrait laid on one. A round crop is what a
|
|
12
|
+
* portrait has been for as long as there have been portraits.
|
|
13
|
+
*
|
|
14
|
+
* `square` cuts the corners off instead, which is what a logo or a repository
|
|
15
|
+
* icon wants: those are drawn to the edges of a rectangle and a round crop eats
|
|
16
|
+
* them.
|
|
17
|
+
*/
|
|
18
|
+
export type AvatarShape = 'circle' | 'square';
|
|
19
|
+
export interface AvatarProps extends Omit<React.ComponentPropsWithoutRef<'span'>, 'color'> {
|
|
20
|
+
/**
|
|
21
|
+
* The picture. Until it loads — and forever, if it fails — the fallback is
|
|
22
|
+
* what is drawn, so an avatar is never an empty box.
|
|
23
|
+
*/
|
|
24
|
+
src?: string;
|
|
25
|
+
/** Candidate images at other resolutions, as on any `<img>`. */
|
|
26
|
+
srcSet?: string;
|
|
27
|
+
/**
|
|
28
|
+
* What the picture says, for a reader who cannot see it. Defaults to `name`,
|
|
29
|
+
* and to nothing at all when there is no name — an avatar next to the person's
|
|
30
|
+
* own name in the row is decoration, and reading it out says the name twice.
|
|
31
|
+
*/
|
|
32
|
+
alt?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Who or what this is. One prop doing three jobs: it names the picture, the
|
|
35
|
+
* initials are derived from it, and it is the sentence a screen reader hears
|
|
36
|
+
* instead of those initials.
|
|
37
|
+
*
|
|
38
|
+
* The initials are the first character of the first word plus the first
|
|
39
|
+
* character of the last — "Jane Doe" is `JD`, "홍길동" is `홍`. That rule is
|
|
40
|
+
* wrong for some names, which is what `initials` is for.
|
|
41
|
+
*/
|
|
42
|
+
name?: string;
|
|
43
|
+
/** The initials, written out, for when the rule derived the wrong ones. */
|
|
44
|
+
initials?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The crop.
|
|
47
|
+
* @default 'circle'
|
|
48
|
+
*/
|
|
49
|
+
shape?: AvatarShape;
|
|
50
|
+
/**
|
|
51
|
+
* Weight of the surface behind the fallback. Invisible once a picture has
|
|
52
|
+
* loaded, apart from the edge it keeps.
|
|
53
|
+
* @default 'text'
|
|
54
|
+
*/
|
|
55
|
+
variant?: NebaVariant;
|
|
56
|
+
/**
|
|
57
|
+
* The box the picture is drawn in — the control heights, so an avatar and the
|
|
58
|
+
* button beside it in a toolbar are the same height.
|
|
59
|
+
* @default 'md'
|
|
60
|
+
*/
|
|
61
|
+
size?: NebaSize;
|
|
62
|
+
/** @default 'primary' */
|
|
63
|
+
color?: NebaColor;
|
|
64
|
+
/**
|
|
65
|
+
* Drop shadow depth. `0` (the default) is flat.
|
|
66
|
+
* @default 0
|
|
67
|
+
*/
|
|
68
|
+
elevation?: NebaElevation;
|
|
69
|
+
/**
|
|
70
|
+
* How long to wait before drawing the fallback, in milliseconds. Set it to
|
|
71
|
+
* roughly the time a cached image takes and the initials stop flashing up in
|
|
72
|
+
* front of a picture that was about to arrive anyway.
|
|
73
|
+
*/
|
|
74
|
+
delay?: number;
|
|
75
|
+
/** Anything else the `<img>` needs — `loading`, `crossOrigin`, `referrerPolicy`. */
|
|
76
|
+
imageProps?: Omit<React.ComponentPropsWithoutRef<'img'>, 'src' | 'srcSet' | 'alt'>;
|
|
77
|
+
/** Called as the picture moves between `idle`, `loading`, `loaded` and `error`. */
|
|
78
|
+
onLoadingStatusChange?: (status: AvatarLoadingStatus) => void;
|
|
79
|
+
/**
|
|
80
|
+
* The fallback, drawn instead of the initials. An icon, a logo, a single
|
|
81
|
+
* emoji — whatever stands in for this particular thing when there is no
|
|
82
|
+
* picture of it.
|
|
83
|
+
*/
|
|
84
|
+
children?: React.ReactNode;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A picture of a person or a thing, at a known size, that is never an empty box.
|
|
88
|
+
*
|
|
89
|
+
* Three things can be drawn in it and exactly one of them is at a time: the
|
|
90
|
+
* picture, if `src` is given and loads; otherwise whatever stands in for it —
|
|
91
|
+
* `children`, or `initials`, or the initials derived from `name`; and failing
|
|
92
|
+
* all of those, a silhouette. Which one is showing is Base UI's `Avatar` to
|
|
93
|
+
* decide, because "has the image loaded" is a question with four answers and a
|
|
94
|
+
* race in the middle of it.
|
|
95
|
+
*
|
|
96
|
+
* It carries no status dot of its own. An avatar with a green mark on it is a
|
|
97
|
+
* [Badge](./badge) with an avatar in it, and inventing a second spelling for
|
|
98
|
+
* that would give the library two of them.
|
|
99
|
+
*/
|
|
100
|
+
export declare const Avatar: React.ForwardRefExoticComponent<AvatarProps & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as t from"react";import{Avatar as n}from"@base-ui/react/avatar";import{controlHeightClasses as a,controlSlots as o,controlSquareClasses as l,hasContent as s,srOnlyClasses as i,surfaceClasses as d,transitionClasses as c}from"../../internal/styles";const m={xs:"text-[0.5625rem]",sm:"text-[0.6875rem]",md:"text-[0.8125rem]",lg:"text-[1rem]",xl:"text-[1.1875rem]"},f={xs:"rounded-[0.375rem]",sm:"rounded-[0.4375rem]",md:"rounded-[0.5625rem]",lg:"rounded-[0.6875rem]",xl:"rounded-[0.875rem]"},u={solid:[d,"text-(--n-on-solid) bg-(--n-fill)","[box-shadow:var(--n-elev)]"].join(" "),outline:[d,"border text-(--n-accent) bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev)]"].join(" "),text:"text-(--n-accent) bg-(--n-soft-press) [box-shadow:var(--n-elev)]"},x="after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit]",h={solid:`${x} after:[box-shadow:var(--neba-plate-solid)]`,outline:`${x} after:[box-shadow:var(--neba-plate-glass)]`,text:""},v=["relative inline-flex shrink-0 select-none items-center justify-center overflow-hidden","align-middle font-semibold tracking-wide whitespace-nowrap leading-none","[&_svg]:pointer-events-none [&_svg]:size-[55%]",c].join(" ");function p(){return r("svg",{viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",children:[e("path",{d:"M12 12a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9Z"}),e("path",{d:"M12 14.25c-4.28 0-7.75 2.42-7.75 5.4 0 .75.6 1.35 1.35 1.35h12.8c.75 0 1.35-.6 1.35-1.35 0-2.98-3.47-5.4-7.75-5.4Z"})]})}export const Avatar=t.forwardRef(function({src:t,srcSet:d,alt:c,name:x,initials:g,shape:b="circle",variant:w="text",size:j="md",color:y="primary",elevation:N=0,delay:z,imageProps:C,onLoadingStatusChange:A,className:B,style:S,children:k,...L},F){const M=x?function(e){const r=e.normalize("NFC").trim().split(/\s+/).filter(Boolean);return 0===r.length?"":((Array.from(r[0])[0]??"")+(r.length>1?Array.from(r[r.length-1])[0]??"":"")).toLocaleUpperCase()}(x):"",R=c??x,Z=s(k)?k:(g??M)||e(p,{}),$=s(k)||Boolean(g??M),_=[v,a[j],l[j],m[j],"circle"===b?"rounded-full":f[j],u[w],h[w],B??""].filter(Boolean).join(" ");return r(n.Root,{ref:F,className:_,style:{...o(y,N,w),...S},...L,children:[t?e(n.Image,{src:t,srcSet:d,alt:R??"",className:"size-full object-cover",onLoadingStatusChange:A,...C}):null,r(n.Fallback,{delay:t?z:void 0,className:"flex size-full items-center justify-center",children:[R&&$?e("span",{className:i,children:R}):null,e("span",{"aria-hidden":!(!R||!$)||void 0,className:"contents",children:Z})]})]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Avatar}from"./Avatar";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* The props are a `<figure>`'s rather than a `<blockquote>`'s, which is a
|
|
5
|
+
* consequence of where the drawing happens: everything a caller passes lands on
|
|
6
|
+
* the wrapper, and the wrapper is a figure or a div. Both are `HTMLElement`, so
|
|
7
|
+
* an event handler written against one works on the other.
|
|
8
|
+
*/
|
|
9
|
+
export interface BlockquoteProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'figure'>, 'color'> {
|
|
10
|
+
/**
|
|
11
|
+
* Drop shadow depth. `0` (the default) is flat — a quote is set *into* a page
|
|
12
|
+
* rather than floating over it, so this is raised even less often than on a
|
|
13
|
+
* Card.
|
|
14
|
+
* @default 0
|
|
15
|
+
*/
|
|
16
|
+
elevation?: NebaElevation;
|
|
17
|
+
/**
|
|
18
|
+
* Who said it. Its presence is what turns the quote into a `<figure>` with a
|
|
19
|
+
* `<figcaption>`, which is the markup the HTML spec asks for: an attribution
|
|
20
|
+
* is *about* the quote and is not part of what was said.
|
|
21
|
+
*/
|
|
22
|
+
author?: React.ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Where it is from — a book, a talk, a page. Rendered inside a `<cite>`, which
|
|
25
|
+
* is the element for the title of a work and, per the spec, never for the name
|
|
26
|
+
* of a person. That is what `author` is.
|
|
27
|
+
*/
|
|
28
|
+
source?: React.ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* URL of the document the quote was taken from. Lands on the `<blockquote>`'s
|
|
31
|
+
* own `cite` attribute, which is machine-readable and shown to nobody — use
|
|
32
|
+
* `source` for the part a reader should see.
|
|
33
|
+
*/
|
|
34
|
+
cite?: string;
|
|
35
|
+
/**
|
|
36
|
+
* The mark drawn before the quote. Omit it for the house glyph, pass a node to
|
|
37
|
+
* replace it, pass `false` to take it away — the same three-way spelling Alert
|
|
38
|
+
* uses for the same idea.
|
|
39
|
+
*/
|
|
40
|
+
icon?: React.ReactNode | false;
|
|
41
|
+
/** What was said. */
|
|
42
|
+
children?: React.ReactNode;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Somebody else's words, set apart from your own.
|
|
46
|
+
*
|
|
47
|
+
* There is no Base UI primitive under this and there should not be: a quote has
|
|
48
|
+
* no state, no keyboard contract and nothing to interact with. What it has is
|
|
49
|
+
* markup that is easy to get wrong, and getting it right is most of the point.
|
|
50
|
+
*
|
|
51
|
+
* **Nothing is drawn on the `<blockquote>` itself.** The surface, the rule and
|
|
52
|
+
* the padding all belong to the element around it, and that is not tidiness —
|
|
53
|
+
* `blockquote` is one of the handful of tags a host stylesheet still styles by
|
|
54
|
+
* name. VitePress's `.vp-doc blockquote` sets a grey `border-left`, a
|
|
55
|
+
* `padding-left` and a `color`, all at a specificity a one-class utility cannot
|
|
56
|
+
* outrank, so a rule drawn on the quote itself would silently come out grey and
|
|
57
|
+
* a pixel too thin. Moving the drawing onto a wrapper is what lets the docs undo
|
|
58
|
+
* VitePress's version in `scope.css` without also undoing ours.
|
|
59
|
+
*
|
|
60
|
+
* The wrapper is a `<figure>` when there is an attribution and a `<div>` when
|
|
61
|
+
* there is not, because the HTML spec is explicit that the attribution goes
|
|
62
|
+
* *outside* the blockquote — a name inside it claims the speaker said their own
|
|
63
|
+
* name — and a `<figure>` with no `<figcaption>` in it is a figure of nothing.
|
|
64
|
+
*/
|
|
65
|
+
export declare const Blockquote: React.ForwardRefExoticComponent<BlockquoteProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as t from"react";import{boxPaddingClasses as n}from"../box/Box";import{hasContent as l,metaTextClasses as a,radiusClasses as o,surfaceClasses as i,surfaceSlots as s,transitionClasses as c}from"../../internal/styles";const m={xs:"text-[0.75rem]/[1.25rem]",sm:"text-[0.8125rem]/[1.375rem]",md:"text-[0.9375rem]/[1.625rem]",lg:"text-[1.0625rem]/[1.875rem]",xl:"text-[1.25rem]/[2.125rem]"},d="border-s-2 [border-inline-start-color:var(--n-accent)]",x={solid:[i,"bg-(--n-panel-hover)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[i,"border border-s-2 bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:"bg-transparent"};function f(){return e("svg",{viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:e("path",{d:"M6.4 3.6c-2.3.9-3.7 2.8-3.7 5.1 0 2 1.2 3.3 2.8 3.3 1.4 0 2.5-1 2.5-2.4 0-1.3-.9-2.2-2.1-2.2-.2 0-.4 0-.6.1.3-1 1.1-1.8 2.2-2.3l-1.1-1.6ZM13.3 3.6c-2.3.9-3.7 2.8-3.7 5.1 0 2 1.2 3.3 2.8 3.3 1.4 0 2.5-1 2.5-2.4 0-1.3-.9-2.2-2.1-2.2-.2 0-.4 0-.6.1.3-1 1.1-1.8 2.2-2.3l-1.1-1.6Z",fill:"currentColor"})})}export const Blockquote=t.forwardRef(function({variant:t="text",size:i="md",color:u="primary",density:b="default",elevation:p=0,author:h,source:v,cite:g,icon:N,className:j,style:y,children:w,...B},k){const z=l(h)||l(v),q=void 0===N?e(f,{}):N,M=["flex flex-col text-(--neba-fg)",d,"text"===t?"":`${o[i]} rounded-s-none`,x[t],n[b][i],c,j??""].filter(Boolean).join(" "),Z=r("blockquote",{cite:g,className:m[i],children:[l(q)?e("span",{"aria-hidden":"true",className:"mb-1 block size-[2em] text-(--n-soft-press) [&>svg]:size-full",children:q}):null,w]}),C={...s(u,p),...y};return z?r("figure",{ref:k,className:M,style:C,...B,children:[Z,r("figcaption",{className:["mt-2 flex flex-wrap items-baseline gap-x-1.5 text-(--neba-muted-fg)",a[i]].join(" "),children:[l(h)?r("span",{className:"font-medium text-(--neba-fg)",children:[e("span",{"aria-hidden":"true",children:"— "}),h]}):null,l(v)?e("cite",{className:"not-italic",children:v}):null]})]}):e("div",{ref:k,className:M,style:C,...B,children:Z})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Blockquote}from"./Blockquote";
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaColor, NebaDensity, NebaSize } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* What is drawn between two steps of the trail.
|
|
5
|
+
*
|
|
6
|
+
* Four named marks rather than a free-for-all, because a separator is read
|
|
7
|
+
* hundreds of times a day and the difference between them is meaning, not
|
|
8
|
+
* decoration: a `chevron` and an `arrow` say "and then", a `slash` says "path",
|
|
9
|
+
* a `dot` says "these are peers of one thing". Anything else can still be passed
|
|
10
|
+
* as a node.
|
|
11
|
+
*/
|
|
12
|
+
export type BreadcrumbSeparator = 'chevron' | 'arrow' | 'slash' | 'dot';
|
|
13
|
+
export interface BreadcrumbProps extends Omit<React.ComponentPropsWithoutRef<'nav'>, 'color'> {
|
|
14
|
+
/** @default 'md' */
|
|
15
|
+
size?: NebaSize;
|
|
16
|
+
/** The colour family a link picks up when it is hovered. @default 'primary' */
|
|
17
|
+
color?: NebaColor;
|
|
18
|
+
/** How tightly the steps pack. Spacing only. @default 'default' */
|
|
19
|
+
density?: NebaDensity;
|
|
20
|
+
/**
|
|
21
|
+
* The mark between two steps. One of the four names, or any node.
|
|
22
|
+
* @default 'chevron'
|
|
23
|
+
*/
|
|
24
|
+
separator?: BreadcrumbSeparator | React.ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* How many steps to show before the middle is folded away behind a `…`.
|
|
27
|
+
* Left out, the whole trail is shown however long it gets.
|
|
28
|
+
*/
|
|
29
|
+
maxItems?: number;
|
|
30
|
+
/** How many steps stay at the front of a folded trail. @default 1 */
|
|
31
|
+
itemsBeforeCollapse?: number;
|
|
32
|
+
/** How many stay at the end. @default 1 */
|
|
33
|
+
itemsAfterCollapse?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Whether pressing the `…` unfolds the trail in place. Turn it off to leave
|
|
36
|
+
* the fold as a plain mark.
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
expandable?: boolean;
|
|
40
|
+
/** The name the trail is announced by. @default 'Breadcrumb' */
|
|
41
|
+
label?: string;
|
|
42
|
+
/** What the `…` is announced as. @default 'Show hidden steps' */
|
|
43
|
+
expandLabel?: string;
|
|
44
|
+
/** The BreadcrumbItems. */
|
|
45
|
+
children?: React.ReactNode;
|
|
46
|
+
}
|
|
47
|
+
export interface BreadcrumbItemProps extends Omit<React.ComponentPropsWithoutRef<'li'>, 'color' | 'onClick'> {
|
|
48
|
+
/** Renders the step as a link. */
|
|
49
|
+
href?: string;
|
|
50
|
+
/** Fires when the step is pressed. Renders it as a button when there is no `href`. */
|
|
51
|
+
onClick?: React.MouseEventHandler<HTMLElement>;
|
|
52
|
+
/** Content before the label — a home glyph, a repository avatar. */
|
|
53
|
+
startIcon?: React.ReactNode;
|
|
54
|
+
/** Content after the label. */
|
|
55
|
+
endIcon?: React.ReactNode;
|
|
56
|
+
/**
|
|
57
|
+
* Marks this step as the page you are on, which stops it being a link.
|
|
58
|
+
*
|
|
59
|
+
* The last step is the current one on its own, so this is only needed for a
|
|
60
|
+
* trail that ends somewhere the reader is not — and setting it anywhere takes
|
|
61
|
+
* the mark off the last step, because only one step in a trail can be it.
|
|
62
|
+
*/
|
|
63
|
+
current?: boolean;
|
|
64
|
+
/** Unavailable. Stops answering, keeps its place in the trail. */
|
|
65
|
+
disabled?: boolean;
|
|
66
|
+
/** The step's label. */
|
|
67
|
+
children?: React.ReactNode;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The trail of pages above the one being read.
|
|
71
|
+
*
|
|
72
|
+
* Two things make this more than a row of links. The first is that the last step
|
|
73
|
+
* is where the reader already is, so it is not a link at all — it carries
|
|
74
|
+
* `aria-current="page"` and stops being pressable, and the component works that
|
|
75
|
+
* out rather than asking every caller to remember it. The second is the fold: a
|
|
76
|
+
* trail seven levels deep is a trail nobody reads, so the middle collapses to a
|
|
77
|
+
* `…` that puts it back when pressed.
|
|
78
|
+
*
|
|
79
|
+
* The separators are drawn by the trail rather than by the steps. A step does
|
|
80
|
+
* not know whether anything follows it, and a mark that belonged to a step would
|
|
81
|
+
* have to be taken off the last one by hand.
|
|
82
|
+
*/
|
|
83
|
+
export declare const Breadcrumb: React.ForwardRefExoticComponent<BreadcrumbProps & React.RefAttributes<HTMLElement>>;
|
|
84
|
+
/**
|
|
85
|
+
* One step of the trail.
|
|
86
|
+
*
|
|
87
|
+
* It renders three different things and the caller picks by what they pass: an
|
|
88
|
+
* `<a>` with an `href`, a `<button>` with an `onClick`, and a plain `<span>` with
|
|
89
|
+
* neither — which is what the last step is, because the page you are already on
|
|
90
|
+
* is not somewhere to go.
|
|
91
|
+
*/
|
|
92
|
+
export declare const BreadcrumbItem: React.ForwardRefExoticComponent<BreadcrumbItemProps & React.RefAttributes<HTMLLIElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as a,Fragment as n}from"react/jsx-runtime";import*as t from"react";import{ArrowRightIcon as s,ChevronIcon as r,EllipsisIcon as l}from"../../internal/icons";import{controlTextClasses as i,cx as c,gapClasses as o,hasContent as m,iconClasses as d,radiusClasses as f,transitionClasses as u}from"../../internal/styles";const h=t.createContext({size:"md",last:!1}),p={default:{xs:"gap-1",sm:"gap-1.5",md:"gap-2",lg:"gap-2.5",xl:"gap-3"},compact:{xs:"gap-0.5",sm:"gap-1",md:"gap-1",lg:"gap-1.5",xl:"gap-2"}},x={xs:f.xs,sm:f.xs,md:f.xs,lg:f.sm,xl:f.sm};const b=["chevron","arrow","slash","dot"];export const Breadcrumb=t.forwardRef(function({size:n="md",color:o="primary",density:m="default",separator:f="chevron",maxItems:x,itemsBeforeCollapse:g=1,itemsAfterCollapse:v=1,expandable:N=!0,label:y="Breadcrumb",expandLabel:w="Show hidden steps",className:k,style:C,children:B,...z},I){const[_,j]=t.useState(!1),M=t.Children.toArray(B).filter(t.isValidElement),$=M.length,A=M.some(e=>!0===e.props.current),F=!_&&void 0!==x&&$>Math.max(x,1)&&$-g-v>1?[...M.slice(0,Math.max(0,g)),null,...M.slice($-Math.max(0,v))]:M,R="string"==typeof(S=f)&&b.includes(S)?function(a){switch(a){case"chevron":return e("span",{className:"flex -rotate-90 items-center rtl:rotate-90",children:e(r,{})});case"arrow":return e("span",{className:"flex items-center rtl:rotate-180",children:e(s,{})});case"slash":return e("span",{className:"opacity-70",children:"/"});case"dot":return e("span",{className:"opacity-70",children:"·"});default:return null}}(f):f;var S;const E=c("inline-flex items-center rounded-(--neba-radius-xs) px-0.5","text-(--neba-muted-fg)",u,d,N?c("cursor-pointer hover:bg-(--n-soft) hover:text-(--n-accent)","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-1"):"");return e("nav",{ref:I,"aria-label":y,className:c("flex",i[n],d,k),style:{"--n-accent":`var(--neba-${o}-accent)`,"--n-soft":`var(--neba-${o}-soft)`,"--n-ring":`var(--neba-${o}-ring)`,...C},...z,children:e("ol",{role:"list",className:c("flex list-none flex-wrap items-center p-0",p[m][n]),children:F.map((s,r)=>a(t.Fragment,{children:[r>0?e("li",{"aria-hidden":"true",className:"flex shrink-0 items-center text-(--neba-muted-fg) select-none",children:R}):null,s?e(h.Provider,{value:{size:n,last:!A&&r===F.length-1},children:s}):e("li",{className:"flex shrink-0 items-center",children:N?e("button",{type:"button",className:E,"aria-label":w,onClick:()=>j(!0),children:e(l,{})}):e("span",{className:E,"aria-hidden":"true",children:e(l,{})})})]},s?s.key??r:"fold"))})})});export const BreadcrumbItem=t.forwardRef(function({href:s,onClick:r,startIcon:l,endIcon:i,current:d,disabled:f=!1,className:p,children:b,...g},v){const{size:N,last:y}=t.useContext(h),w=d??y,k=Boolean(s||r)&&!w&&!f,C=c("inline-flex min-w-0 items-center px-1",o[N],x[N],u,f?"cursor-not-allowed text-(--neba-disabled-fg)":w?"font-medium text-(--neba-fg)":k?c("cursor-pointer text-(--neba-muted-fg)","hover:bg-(--n-soft) hover:text-(--n-accent)","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-1"):"text-(--neba-muted-fg)"),B=a(n,{children:[m(l)?e("span",{className:"flex h-[1lh] shrink-0 items-center",children:l}):null,e("span",{className:"truncate",children:b}),m(i)?e("span",{className:"flex h-[1lh] shrink-0 items-center",children:i}):null]});return e("li",{ref:v,className:c("flex min-w-0 items-center",p),...g,children:k&&s?e("a",{href:s,className:C,onClick:r,children:B}):k?e("button",{type:"button",className:C,onClick:r,children:B}):e("span",{className:C,"aria-current":w?"page":void 0,"aria-disabled":f||void 0,children:B})})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Breadcrumb,BreadcrumbItem}from"./Breadcrumb";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
export interface CarouselProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'defaultValue' | 'onChange'> {
|
|
4
|
+
/**
|
|
5
|
+
* Drop shadow depth of the frame. `0` — the default — is flat.
|
|
6
|
+
* @default 0
|
|
7
|
+
*/
|
|
8
|
+
elevation?: NebaElevation;
|
|
9
|
+
/** Which slide is showing, counted from 0. Use with `onValueChange`. */
|
|
10
|
+
value?: number;
|
|
11
|
+
/** Which starts showing, for an uncontrolled carousel. @default 0 */
|
|
12
|
+
defaultValue?: number;
|
|
13
|
+
onValueChange?: (index: number) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the arrows wrap from the last slide back to the first. With it off
|
|
16
|
+
* they go inert at the ends instead, which is the honest thing for a set that
|
|
17
|
+
* has a beginning and an end — a gallery of three photographs does, a rotating
|
|
18
|
+
* banner does not.
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
loop?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Advances on its own.
|
|
24
|
+
*
|
|
25
|
+
* Off by default and deliberately so: a carousel that moves while it is being
|
|
26
|
+
* read is the most complained-about pattern on the web. It pauses on hover,
|
|
27
|
+
* on focus anywhere inside it, while the tab is in the background, and it does
|
|
28
|
+
* not start at all for a reader who has asked for reduced motion.
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
autoPlay?: boolean;
|
|
32
|
+
/** How long each slide is held, in milliseconds. @default 5000 */
|
|
33
|
+
interval?: number;
|
|
34
|
+
/** The previous/next buttons. @default true */
|
|
35
|
+
arrows?: boolean;
|
|
36
|
+
/** The row of position dots under the frame. @default true */
|
|
37
|
+
indicators?: boolean;
|
|
38
|
+
/** The carousel's accessible name. @default 'Carousel' */
|
|
39
|
+
label?: string;
|
|
40
|
+
/** @default 'Previous slide' */
|
|
41
|
+
previousLabel?: string;
|
|
42
|
+
/** @default 'Next slide' */
|
|
43
|
+
nextLabel?: string;
|
|
44
|
+
/**
|
|
45
|
+
* How one slide is named to a screen reader, and how its dot is labelled.
|
|
46
|
+
* @default (index, count) => `Slide ${index} of ${count}`
|
|
47
|
+
*/
|
|
48
|
+
slideLabel?: (index: number, count: number) => string;
|
|
49
|
+
/** The slides. Every top-level child becomes one. */
|
|
50
|
+
children?: React.ReactNode;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A strip of slides, one of which is in view.
|
|
54
|
+
*
|
|
55
|
+
* The mechanism is a scroll container with CSS scroll snapping, and everything
|
|
56
|
+
* good about this component follows from that one choice. Swiping on a phone and
|
|
57
|
+
* two-finger dragging on a trackpad both work because they are the browser's own
|
|
58
|
+
* scrolling and not a gesture handler pretending to be it. The strip runs the
|
|
59
|
+
* other way under RTL without being told, because scrolling is directional and
|
|
60
|
+
* `translate` is not. And nothing is transformed — the house rule against moving
|
|
61
|
+
* a surface holds here for free, where a translated track would have had to
|
|
62
|
+
* argue for an exception.
|
|
63
|
+
*
|
|
64
|
+
* The motion is `scroll-behavior: smooth`, which means a reader who has asked
|
|
65
|
+
* for reduced motion gets an instant cut from the same code path rather than
|
|
66
|
+
* from a second one written to remember them.
|
|
67
|
+
*
|
|
68
|
+
* Slides are not a sub-component. Every top-level child is wrapped in its own
|
|
69
|
+
* slide, so `<Carousel><img /><img /></Carousel>` is the whole API — and the
|
|
70
|
+
* wrapper is what carries the snap point, the width and the
|
|
71
|
+
* `role="group"`/`aria-roledescription="slide"` pair a screen reader needs, none
|
|
72
|
+
* of which a caller should have to remember to put on a photograph.
|
|
73
|
+
*/
|
|
74
|
+
export declare const Carousel: React.ForwardRefExoticComponent<CarouselProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as t from"react";import{IconButton as n}from"../icon-button/IconButton";import{ChevronIcon as a}from"../../internal/icons";import{radiusClasses as o,srOnlyClasses as i,surfaceClasses as l,surfaceSlots as s,transitionClasses as c}from"../../internal/styles";const u={solid:[l,"text-(--neba-fg) bg-(--n-panel-hover)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[l,"border text-(--neba-fg) bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:"text-(--neba-fg) bg-transparent"},d={xs:"start-1 end-1",sm:"start-1.5 end-1.5",md:"start-2 end-2",lg:"start-3 end-3",xl:"start-4 end-4"},f={xs:{rest:"h-1 w-1",current:"h-1 w-3",gap:"gap-1"},sm:{rest:"h-1 w-1",current:"h-1 w-3.5",gap:"gap-1"},md:{rest:"h-1.5 w-1.5",current:"h-1.5 w-4",gap:"gap-1.5"},lg:{rest:"h-1.5 w-1.5",current:"h-1.5 w-5",gap:"gap-2"},xl:{rest:"h-2 w-2",current:"h-2 w-6",gap:"gap-2"}},m=(e,r)=>`Slide ${e} of ${r}`;export const Carousel=t.forwardRef(function({variant:l="outline",size:p="md",color:v="primary",density:h="default",elevation:b=0,value:w,defaultValue:g=0,onValueChange:x,loop:N=!0,autoPlay:y=!1,interval:j=5e3,arrows:k=!0,indicators:C=!0,label:M="Carousel",previousLabel:R="Previous slide",nextLabel:$="Next slide",slideLabel:I=m,className:L,style:P,children:S,..._},z){const B=t.Children.toArray(S),E=B.length,[V,T]=t.useState(g),W=Math.min(Math.max(w??V,0),Math.max(E-1,0)),A=t.useRef(null),F=t.useRef([]),q=t.useRef(!1),D=t.useRef(!1),G=t.useRef(!1),[H,J]=t.useState(!1),K=t.useCallback((e,r=!1)=>{if(0===E)return;const t=N?(e%E+E)%E:Math.min(Math.max(e,0),E-1);q.current=r,void 0===w&&T(t),t!==W&&x?.(t)},[E,N,w,W,x]);t.useEffect(()=>{if(q.current)return void(q.current=!1);if(!D.current)return void(D.current=!0);F.current[W]?.scrollIntoView({block:"nearest",inline:"start"}),G.current=!0;const e=window.setTimeout(()=>{G.current=!1},700);return()=>window.clearTimeout(e)},[W]);t.useEffect(()=>{if(!y||H||E<2)return;if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)return;const e=window.setInterval(()=>{document.hidden||K(W+1)},j);return()=>window.clearInterval(e)},[y,H,E,j,W,K]);const O=W<=0,Q=W>=E-1;return r("div",{ref:z,role:"region","aria-roledescription":"carousel","aria-label":M,className:["flex flex-col",L??""].filter(Boolean).join(" "),style:{...s(v,b),...P},onPointerEnter:()=>J(!0),onPointerLeave:()=>J(!1),onFocus:()=>J(!0),onBlur:()=>J(!1),..._,children:[r("div",{className:["relative min-w-0 overflow-hidden",o[p],u[l],c].join(" "),children:[e("div",{ref:A,tabIndex:0,role:"group","aria-label":M,className:["flex min-w-0 snap-x snap-mandatory overflow-x-auto scroll-smooth","motion-reduce:scroll-auto","[scrollbar-width:none] [&::-webkit-scrollbar]:hidden","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:[outline-offset:-2px]"].join(" "),onScroll:()=>{const e=A.current;if(!e||0===e.clientWidth||G.current)return;const r=Math.round(Math.abs(e.scrollLeft)/e.clientWidth);r!==W&&r>=0&&r<E&&K(r,!0)},children:B.map((r,t)=>e("div",{ref:e=>{F.current[t]=e},role:"group","aria-roledescription":"slide","aria-label":I(t+1,E),className:"w-full shrink-0 grow-0 basis-full snap-start",children:r},t))}),k&&E>1?r("div",{className:`pointer-events-none absolute inset-y-0 flex items-center ${d[p]}`,children:[e(n,{variant:"solid",size:p,color:v,density:h,elevation:1,label:R,disabled:!N&&O,className:"pointer-events-auto",icon:e("span",{className:"flex items-center rotate-90 rtl:-rotate-90",children:e(a,{})}),onClick:()=>K(W-1)}),e("span",{className:"flex-1"}),e(n,{variant:"solid",size:p,color:v,density:h,elevation:1,label:$,disabled:!N&&Q,className:"pointer-events-auto",icon:e("span",{className:"flex items-center -rotate-90 rtl:rotate-90",children:e(a,{})}),onClick:()=>K(W+1)})]}):null]}),C&&E>1?e("div",{className:`flex shrink-0 items-center justify-center pt-2 ${f[p].gap}`,children:B.map((r,t)=>e("button",{type:"button","aria-label":I(t+1,E),"aria-current":t===W?"true":void 0,className:["cursor-pointer rounded-full","[transition-property:width,background-color]","[transition-duration:var(--neba-duration)]","[transition-timing-function:var(--neba-ease)]","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-2",t===W?`${f[p].current} bg-(--n-accent)`:`${f[p].rest} bg-(--n-line-hover) hover:bg-(--n-accent)`].join(" "),onClick:()=>K(t)},t))}):null,e("span",{className:i,"aria-live":y?"off":"polite",children:E>0?I(W+1,E):""})]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Carousel}from"./Carousel";
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaColor, NebaDensity, NebaElevation, NebaSize, NebaVariant } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Whose message this is.
|
|
5
|
+
*
|
|
6
|
+
* `start` and `end` rather than `them`/`me` or `left`/`right`: a thread runs the
|
|
7
|
+
* way the language does, and the same two words already mean this everywhere
|
|
8
|
+
* else in the library. `start` is the default because a message from someone
|
|
9
|
+
* else is the one you have no other way of knowing about.
|
|
10
|
+
*/
|
|
11
|
+
export type ChatBubbleSide = 'start' | 'end';
|
|
12
|
+
/**
|
|
13
|
+
* How far a message has got.
|
|
14
|
+
*
|
|
15
|
+
* The four steps are a ladder and the fifth is not on it: `failed` is the
|
|
16
|
+
* message that did not go, which is why it is the only one drawn in another
|
|
17
|
+
* colour family.
|
|
18
|
+
*/
|
|
19
|
+
export type ChatBubbleStatus = 'sending' | 'sent' | 'delivered' | 'read' | 'failed';
|
|
20
|
+
/** What a link inside a message unfurls to. */
|
|
21
|
+
export interface ChatBubbleLinkPreview {
|
|
22
|
+
/** Where the card goes. */
|
|
23
|
+
url: string;
|
|
24
|
+
/** The page's title. */
|
|
25
|
+
title?: React.ReactNode;
|
|
26
|
+
/** Its summary, clamped to two lines. */
|
|
27
|
+
description?: React.ReactNode;
|
|
28
|
+
/** The share image, drawn across the top of the card. */
|
|
29
|
+
image?: string;
|
|
30
|
+
/** Who published it — a domain, a site name. */
|
|
31
|
+
site?: React.ReactNode;
|
|
32
|
+
/** Opens the card in a new tab. @default false */
|
|
33
|
+
newTab?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface ChatBubbleProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'title'> {
|
|
36
|
+
/**
|
|
37
|
+
* Whose message this is.
|
|
38
|
+
* @default 'start'
|
|
39
|
+
*/
|
|
40
|
+
side?: ChatBubbleSide;
|
|
41
|
+
/** Who sent it, above the bubble. */
|
|
42
|
+
name?: React.ReactNode;
|
|
43
|
+
/** When it was sent, beside the name. */
|
|
44
|
+
time?: React.ReactNode;
|
|
45
|
+
/**
|
|
46
|
+
* The sender's picture — an [Avatar](../display/avatar), at the size the
|
|
47
|
+
* thread uses. Left out, the bubble takes the whole row.
|
|
48
|
+
*/
|
|
49
|
+
avatar?: React.ReactNode;
|
|
50
|
+
/**
|
|
51
|
+
* How far the message has got, drawn as a mark under the bubble. Left out,
|
|
52
|
+
* nothing is drawn — a received message has no delivery state worth showing.
|
|
53
|
+
*/
|
|
54
|
+
status?: ChatBubbleStatus;
|
|
55
|
+
/** Overrides the word the mark is read out as. */
|
|
56
|
+
statusLabel?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Draws the three dots instead of the message. What `children` holds is left
|
|
59
|
+
* alone, so the same bubble can go back to it when the message arrives.
|
|
60
|
+
* @default false
|
|
61
|
+
*/
|
|
62
|
+
typing?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* A picture, a video, a map — drawn edge to edge above the text, so the
|
|
65
|
+
* bubble's corners crop it.
|
|
66
|
+
*/
|
|
67
|
+
media?: React.ReactNode;
|
|
68
|
+
/** A link in the message, unfurled into a card under the text. */
|
|
69
|
+
preview?: ChatBubbleLinkPreview;
|
|
70
|
+
/**
|
|
71
|
+
* The message's own actions — a [Menu](../inputs/menu) trigger, most of the
|
|
72
|
+
* time. Sits beside the bubble and stays out of the way until the row is
|
|
73
|
+
* hovered or something in it takes focus.
|
|
74
|
+
*/
|
|
75
|
+
actions?: React.ReactNode;
|
|
76
|
+
/**
|
|
77
|
+
* Which language the marks are read out in — a BCP 47 tag such as `ko`,
|
|
78
|
+
* `pt-BR` or `zh-Hant`. Unsupported tags fall back to English.
|
|
79
|
+
*/
|
|
80
|
+
locale?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Weight of the bubble's surface. `solid` is the usual way to tell your own
|
|
83
|
+
* messages from everyone else's; `side` deliberately does not decide this,
|
|
84
|
+
* because which end is filled is a decision about the product, not about the
|
|
85
|
+
* component.
|
|
86
|
+
* @default 'outline'
|
|
87
|
+
*/
|
|
88
|
+
variant?: NebaVariant;
|
|
89
|
+
/** @default 'md' */
|
|
90
|
+
size?: NebaSize;
|
|
91
|
+
/** @default 'primary' */
|
|
92
|
+
color?: NebaColor;
|
|
93
|
+
/** Padding inside the bubble, and nothing else. @default 'default' */
|
|
94
|
+
density?: NebaDensity;
|
|
95
|
+
/** Drop shadow depth. `0` (the default) is flat. @default 0 */
|
|
96
|
+
elevation?: NebaElevation;
|
|
97
|
+
/** The message. */
|
|
98
|
+
children?: React.ReactNode;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* One message in a conversation.
|
|
102
|
+
*
|
|
103
|
+
* Everything around the bubble is optional and nothing about it is fixed by
|
|
104
|
+
* `side`: the avatar, the sender's name, the time, the delivery mark, the media
|
|
105
|
+
* above the text and the link card below it are each drawn only when they are
|
|
106
|
+
* given something. What `side` decides is which way the row runs and which
|
|
107
|
+
* corner of the sheet is cut short.
|
|
108
|
+
*
|
|
109
|
+
* `variant` is what tells your own messages from everyone else's, and it is
|
|
110
|
+
* deliberately not tied to `side` — filling the right-hand column is a
|
|
111
|
+
* convention, not a law, and a thread that fills neither is a perfectly good
|
|
112
|
+
* thread.
|
|
113
|
+
*/
|
|
114
|
+
export declare const ChatBubble: React.ForwardRefExoticComponent<ChatBubbleProps & React.RefAttributes<HTMLDivElement>>;
|