neba 1.1.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 +9 -6
- 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/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/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/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/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/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/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/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 +8 -0
- package/dist/index.js +1 -1
- package/dist/internal/i18n.d.ts +78 -0
- package/dist/internal/i18n.js +1 -0
- package/dist/internal/icons.d.ts +23 -0
- package/dist/internal/icons.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/tailwind.css +125 -0
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
### π [**neba.cdget.com**](https://neba.cdget.com)
|
|
8
8
|
|
|
9
|
-
[](https://neba.cdget.com/examples/)
|
|
9
|
+
[](https://neba.cdget.com/examples/overview)
|
|
10
10
|
|
|
11
11
|
Live previews and full props for every component. This README is just the quick start.
|
|
12
12
|
|
|
@@ -32,10 +32,11 @@ Everything is documented at **[neba.cdget.com](https://neba.cdget.com)**, where
|
|
|
32
32
|
| --- | --- |
|
|
33
33
|
| [**Getting started**](https://neba.cdget.com/guide/getting-started) | Install and setup, end to end. |
|
|
34
34
|
| [**All components**](https://neba.cdget.com/components/) | Every component, one page each: live previews and the full props table. |
|
|
35
|
-
| [**Examples**](https://neba.cdget.com/examples/) | A whole sample screen
|
|
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
36
|
| [**Design language**](https://neba.cdget.com/design/design-language) | Why a Neba surface looks and behaves the way it does. |
|
|
37
37
|
| [**Prop conventions**](https://neba.cdget.com/design/prop-conventions) | The shared vocabulary every component draws from. |
|
|
38
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. |
|
|
39
40
|
|
|
40
41
|
Also available in Korean / νκ΅μ΄ λ¬Έμ: **[neba.cdget.com/ko/](https://neba.cdget.com/ko/)**
|
|
41
42
|
|
|
@@ -110,15 +111,17 @@ Placement props are logical, not physical β `start`/`end` rather than `left`/`
|
|
|
110
111
|
|
|
111
112
|
### Components
|
|
112
113
|
|
|
113
|
-
**Inputs** β Button, IconButton, ButtonGroup, SegmentedButton, TextField, NumberField, Select, Combobox, Checkbox, RadioGroup, Switch, Slider, Menu (with submenus, checkbox and radio items), ContextMenu, FilePicker, Pagination, DatePicker, TimePicker, DateTimePicker, DateRangePicker
|
|
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
|
|
114
115
|
|
|
115
|
-
**Surfaces** β Box, Card, Accordion, Tabs, Carousel, Toolbar, Pill
|
|
116
|
+
**Surfaces** β Box, Card, Accordion, Tabs, Carousel, Toolbar, Pill, Spoiler, ChatBubble
|
|
116
117
|
|
|
117
|
-
**Display** β Typography, Blockquote, Highlight, Divider, Chip, Badge, Icon, Shortcut, Statistic, List, Table, Timeline
|
|
118
|
+
**Display** β Typography, TextLink, Blockquote, Highlight, Divider, Chip, Badge, Avatar, Icon, Shortcut, Statistic, List, Table, Timeline, Breadcrumb, TreeView
|
|
118
119
|
|
|
119
120
|
**Feedback** β Alert, Dialog, Toast, Tooltip, Overlay, ProgressLinear, ProgressCircular, ProgressBox
|
|
120
121
|
|
|
121
|
-
|
|
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.
|
|
122
125
|
|
|
123
126
|
Each one has its own page β live previews, every prop, and the variations worth seeing β under [**All components**](https://neba.cdget.com/components/).
|
|
124
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,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,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>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as n}from"react/jsx-runtime";import*as r from"react";import{useMessages as a}from"../../internal/i18n";import{CheckIcon as l,ClockIcon as i,LinkIcon as t,severityIcons as o}from"../../internal/icons";import{controlSlots as s,hasContent as c,metaTextClasses as d,radiusClasses as m,sheetBodyClasses as p,sheetHeaderGapClasses as u,srOnlyClasses as b,surfaceClasses as f,transitionClasses as v}from"../../internal/styles";const x={default:{xs:"px-2 py-1",sm:"px-2.5 py-1.5",md:"px-3 py-2",lg:"px-3.5 py-2.5",xl:"px-4 py-3"},compact:{xs:"px-1.5 py-0.5",sm:"px-2 py-1",md:"px-2.5 py-1.5",lg:"px-3 py-1.5",xl:"px-3.5 py-2"}},h={start:"[border-start-start-radius:0.25rem]",end:"[border-start-end-radius:0.25rem]"},g={solid:[f,"text-(--n-on-solid) bg-(--n-fill)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[f,"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-(--n-soft) [box-shadow:var(--n-elev)]"};function y(){return n("svg",{viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:[e("path",{d:"m1.5 8.5 2.75 2.75L9.5 6",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round",strokeLinejoin:"round"}),e("path",{d:"M7 10.75 8.25 12l5.25-5.25",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round",strokeLinejoin:"round"})]})}const w={sending:e(i,{}),sent:e(l,{}),delivered:e(y,{}),read:e(y,{}),failed:o.danger},N={sending:"text-(--neba-muted-fg)",sent:"text-(--neba-muted-fg)",delivered:"text-(--neba-muted-fg)",read:"text-(--n-accent)",failed:"text-(--neba-danger-accent)"},k=["block overflow-hidden rounded-(--neba-radius-sm) border no-underline","[border-color:color-mix(in_oklab,currentColor_18%,transparent)]","[background-color:color-mix(in_oklab,currentColor_7%,transparent)]","hover:[background-color:color-mix(in_oklab,currentColor_12%,transparent)]","[transition-property: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"].join(" "),_=["shrink-0 opacity-0","[transition:opacity_var(--neba-duration)_var(--neba-ease)]","group-hover/bubble:opacity-100 group-focus-within/bubble:opacity-100","[@media(hover:none)]:opacity-100"].join(" ");export const ChatBubble=r.forwardRef(function({side:r="start",name:l,time:i,avatar:t,status:o,statusLabel:f,typing:y=!1,media:k,preview:L,actions:z,locale:B,variant:W="outline",size:M="md",color:R="primary",density:T="default",elevation:$=0,className:q,style:A,children:D,...E},F){const G=a(B),H="end"===r,I=c(l)||c(i),J=y||c(D)||Boolean(L),K=["flex min-w-0 flex-col overflow-hidden",m[M],h[r],g[W],v].join(" "),O=o?f??G.chat[o]:"";return n("div",{ref:F,className:["group/bubble flex w-full items-start gap-2",H?"flex-row-reverse":"",q??""].filter(Boolean).join(" "),style:{...s(R,$,W),...A},...E,children:[c(t)?e("div",{className:"shrink-0",children:t}):null,n("div",{className:["flex min-w-0 max-w-[min(100%,32rem)] flex-col",u[M],H?"items-end":"items-start"].join(" "),children:[I?n("div",{className:`flex items-baseline gap-2 ${d[M]}`,children:[c(l)?e("span",{className:"font-semibold",children:l}):null,c(i)?e("span",{className:"text-(--neba-muted-fg)",children:i}):null]}):null,n("div",{className:"flex min-w-0 items-center gap-1 "+(H?"flex-row-reverse":""),children:[n("div",{className:K,children:[c(k)?e("div",{className:"[&_img]:block [&_img]:w-full [&_video]:block [&_video]:w-full",children:k}):null,J?n("div",{className:[x[T][M],"flex min-w-0 flex-col gap-2",p[M]].join(" "),children:[y?e(j,{label:G.chat.typing}):c(D)?e("div",{className:"min-w-0 break-words whitespace-pre-line",children:D}):null,L?e(C,{preview:L}):null]}):null]}),c(z)?e("div",{className:_,children:z}):null]}),o?n("div",{className:["flex items-center gap-1",d[M],N[o],"[&_svg]:size-[1.15em] [&_svg]:shrink-0"].join(" "),children:[w[o],e("span",{className:b,children:O})]}):null]})]})});function j({label:r}){return n("div",{className:"flex items-center gap-1 py-[0.35em]",role:"status",children:[[0,1,2].map(n=>e("span",{"aria-hidden":"true",className:"neba-typing-dot size-[0.45em] rounded-full",style:{"--n-i":n}},n)),e("span",{className:b,children:r})]})}function C({preview:r}){const{url:a,title:l,description:i,image:o,site:s,newTab:d=!1}=r;return n("a",{href:a,target:d?"_blank":void 0,rel:d?"noopener noreferrer":void 0,className:k,children:[o?e("img",{src:o,alt:"",className:"block h-28 w-full object-cover"}):null,n("div",{className:"flex flex-col gap-0.5 p-2",children:[c(s)?n("span",{className:"flex items-center gap-1 text-[0.85em] opacity-70 [&_svg]:size-[1em] [&_svg]:shrink-0",children:[e(t,{}),e("span",{className:"truncate",children:s})]}):null,c(l)?e("span",{className:"font-semibold",children:l}):null,c(i)?e("span",{className:"line-clamp-2 text-[0.9em] opacity-80",children:i}):null]})]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{ChatBubble}from"./ChatBubble";
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* What may be typed into a slot.
|
|
5
|
+
*
|
|
6
|
+
* `numeric` is the default because that is what a texted code is, and it is also
|
|
7
|
+
* what puts a number pad in front of a phone. `any` accepts whatever the
|
|
8
|
+
* keyboard produces β for a licence key with punctuation in it.
|
|
9
|
+
*/
|
|
10
|
+
export type OtpFieldCharset = 'numeric' | 'alpha' | 'alphanumeric' | 'any';
|
|
11
|
+
export interface OtpFieldProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'defaultValue' | 'onChange' | 'children'> {
|
|
12
|
+
/**
|
|
13
|
+
* Drop shadow depth. `0` (the default) is flat.
|
|
14
|
+
* @default 0
|
|
15
|
+
*/
|
|
16
|
+
elevation?: NebaElevation;
|
|
17
|
+
/**
|
|
18
|
+
* How many characters the code has. Clamped to 2β12: a single box is a
|
|
19
|
+
* TextField, and past twelve the row stops fitting a phone.
|
|
20
|
+
* @default 6
|
|
21
|
+
*/
|
|
22
|
+
length?: number;
|
|
23
|
+
/**
|
|
24
|
+
* What may be typed. Anything rejected is dropped rather than shown, and
|
|
25
|
+
* `onValueInvalid` reports it.
|
|
26
|
+
* @default 'numeric'
|
|
27
|
+
*/
|
|
28
|
+
charset?: OtpFieldCharset;
|
|
29
|
+
/** Hides the characters, the way a password field does. @default false */
|
|
30
|
+
mask?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Splits the row every `groupSize` slots with a separator. `3` on a six digit
|
|
33
|
+
* code gives the familiar two blocks of three.
|
|
34
|
+
*/
|
|
35
|
+
groupSize?: number;
|
|
36
|
+
/** What is drawn between two groups. @default 'β' */
|
|
37
|
+
separator?: React.ReactNode;
|
|
38
|
+
/** The code. Use with `onValueChange` for a controlled field. */
|
|
39
|
+
value?: string;
|
|
40
|
+
/** What it starts as, for an uncontrolled one. */
|
|
41
|
+
defaultValue?: string;
|
|
42
|
+
onValueChange?: (value: string) => void;
|
|
43
|
+
/** Fires once every slot is filled β the moment to verify the code. */
|
|
44
|
+
onComplete?: (value: string) => void;
|
|
45
|
+
/** Fires when typed or pasted text held characters the charset rejects. */
|
|
46
|
+
onValueInvalid?: (value: string) => void;
|
|
47
|
+
/** Submits the owning form as soon as the code is complete. @default false */
|
|
48
|
+
autoSubmit?: boolean;
|
|
49
|
+
/** Label above the row, wired to the slots by Base UI's Field. */
|
|
50
|
+
label?: React.ReactNode;
|
|
51
|
+
/** Helper text below the row. */
|
|
52
|
+
description?: React.ReactNode;
|
|
53
|
+
/** Error message below the row. Its presence also turns the field invalid. */
|
|
54
|
+
error?: React.ReactNode;
|
|
55
|
+
/**
|
|
56
|
+
* Forces the invalid state without a message β for when a form library owns
|
|
57
|
+
* the validity. Defaults to whether `error` has content.
|
|
58
|
+
*/
|
|
59
|
+
invalid?: boolean;
|
|
60
|
+
/** Identifies the field when a form is submitted. */
|
|
61
|
+
name?: string;
|
|
62
|
+
/** The form must have a complete code before it submits. @default false */
|
|
63
|
+
required?: boolean;
|
|
64
|
+
/** Unavailable. Every slot stops answering. */
|
|
65
|
+
disabled?: boolean;
|
|
66
|
+
/** Readable and copyable, but not typeable. @default false */
|
|
67
|
+
readOnly?: boolean;
|
|
68
|
+
/** Puts the caret in the first slot on mount. @default false */
|
|
69
|
+
autoFocus?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A row of one-character slots: a PIN, a texted verification code, an invite
|
|
73
|
+
* key.
|
|
74
|
+
*
|
|
75
|
+
* Base UI owns everything that makes this harder than it looks β one hidden
|
|
76
|
+
* value behind however many inputs, paste spread across the slots from wherever
|
|
77
|
+
* the caret was, backspace stepping back a box, a click landing on the first
|
|
78
|
+
* empty slot rather than the one under the pointer, and the autofill hook that
|
|
79
|
+
* lets a phone offer the code straight from the message.
|
|
80
|
+
*
|
|
81
|
+
* What is here is the ladder a slot is drawn on and the field shell it shares
|
|
82
|
+
* with TextField and Select β a slot is a field-shaped box, and a form holding
|
|
83
|
+
* both should not look like two form kits stacked on each other.
|
|
84
|
+
*/
|
|
85
|
+
export declare const OtpField: React.ForwardRefExoticComponent<OtpFieldProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as t from"react";import{Field as n}from"@base-ui/react/field";import{OTPField as l}from"@base-ui/react/otp-field";import{cx as r,disabledClasses as o,fieldReadOnlyClasses as i,fieldRestClasses as s,metaTextClasses as m,stackGapClasses as d,surfaceSlots as u,transitionClasses as c}from"../../internal/styles";const p={xs:"h-7 w-6",sm:"h-8 w-7",md:"h-10 w-8",lg:"h-12 w-10",xl:"h-14 w-12"},f={xs:"rounded-[0.3125rem]",sm:"rounded-[0.375rem]",md:"rounded-[0.4375rem]",lg:"rounded-[0.5625rem]",xl:"rounded-[0.6875rem]"},x={xs:"text-[0.8125rem]",sm:"text-[0.9375rem]",md:"text-[1.0625rem]",lg:"text-[1.25rem]",xl:"text-[1.5rem]"},g={default:{xs:"gap-1",sm:"gap-1",md:"gap-1.5",lg:"gap-2",xl:"gap-2.5"},compact:{xs:"gap-0.5",sm:"gap-0.5",md:"gap-1",lg:"gap-1",xl:"gap-1.5"}},h={numeric:"numeric",alpha:"alpha",alphanumeric:"alphanumeric",any:"none"};export const OtpField=t.forwardRef(function({variant:b="outline",size:y="md",color:v="primary",density:N="default",elevation:V=0,length:w=6,charset:C="numeric",mask:F=!1,groupSize:M,separator:j="β",value:I,defaultValue:O,onValueChange:R,onComplete:S,onValueInvalid:k,autoSubmit:q=!1,label:z,description:_,error:A,invalid:D,name:E,required:L=!1,disabled:T=!1,readOnly:B=!1,autoFocus:G=!1,className:H,style:J,...K},P){const Q=Math.min(12,Math.max(2,Math.round(w))),U=null!=A&&!1!==A&&""!==A,W=D??U,X=W?"danger":v,Y=r("text-center font-medium [font-family:inherit]",p[y],x[y],f[y],c,"caret-(--n-accent) selection:bg-(--n-soft-press)","focus:[outline:2px_solid_var(--n-ring)] focus:outline-offset-1","focus:[transition-duration:0ms]",T?o[b]:B?i[b]:s[b]),Z=M&&M>0?Math.round(M):0;return a(n.Root,{disabled:T,invalid:W,className:r("inline-flex flex-col align-top",d[y],H),style:{...u(X,V),...J},children:[z?e(n.Label,{className:r(m[y],"font-medium",T?"text-(--neba-disabled-fg)":"text-(--neba-fg)"),children:z}):null,e(l.Root,{ref:P,length:Q,validationType:h[C],mask:F,name:E,required:L,disabled:T,readOnly:B,autoSubmit:q,value:I,defaultValue:O,onValueChange:e=>R?.(e),onValueComplete:e=>S?.(e),onValueInvalid:e=>k?.(e),className:r("flex items-center",g[N][y]),...K,children:Array.from({length:Q},(n,o)=>a(t.Fragment,{children:[Z>0&&o>0&&o%Z===0?e("span",{"aria-hidden":"true",className:r("select-none px-0.5 text-(--neba-muted-fg)",x[y]),children:j}):null,e(l.Input,{className:Y,autoFocus:G&&0===o})]},o))}),_?e(n.Description,{className:r(m[y],"text-(--neba-muted-fg)"),children:_}):null,U?e(n.Error,{match:!0,className:r(m[y],"text-(--n-accent)"),children:A}):null]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{OtpField}from"./OtpField";
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaColor, NebaOrientation, NebaSize } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* A pane's share of the split, as a percentage of the container or as a CSS
|
|
5
|
+
* length.
|
|
6
|
+
*
|
|
7
|
+
* A bare number is a percentage β that is what a split is usually described in,
|
|
8
|
+
* and a percentage keeps its meaning when the window changes size. A string is
|
|
9
|
+
* an absolute length (`'240px'`, `'15rem'`, `'20%'`), which is what a sidebar
|
|
10
|
+
* with a minimum actually needs: "at least 200 pixels" does not survive being
|
|
11
|
+
* written down as a percentage of a width nobody knows yet.
|
|
12
|
+
*/
|
|
13
|
+
export type PaneSize = number | string;
|
|
14
|
+
export interface PanesProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
|
15
|
+
/**
|
|
16
|
+
* Which way the panes run. `horizontal` puts them side by side with upright
|
|
17
|
+
* handles between them; `vertical` stacks them.
|
|
18
|
+
* @default 'horizontal'
|
|
19
|
+
*/
|
|
20
|
+
orientation?: NebaOrientation;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the handles between the panes can be dragged. Turn it off for a
|
|
23
|
+
* split that is a layout rather than a control.
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
resizable?: boolean;
|
|
27
|
+
/** The colour family the handles light up in. @default 'primary' */
|
|
28
|
+
color?: NebaColor;
|
|
29
|
+
/** How thick a handle is, and how far it reaches. @default 'md' */
|
|
30
|
+
size?: NebaSize;
|
|
31
|
+
/** Fires with every pane's share, in percent, while a handle is dragged. */
|
|
32
|
+
onResize?: (sizes: number[]) => void;
|
|
33
|
+
/** Fires once, with the same shape, when the handle is let go. */
|
|
34
|
+
onResizeEnd?: (sizes: number[]) => void;
|
|
35
|
+
/** The Panes. Anything that is not a Pane is still laid out, but has no size. */
|
|
36
|
+
children?: React.ReactNode;
|
|
37
|
+
}
|
|
38
|
+
export interface PaneProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
|
39
|
+
/**
|
|
40
|
+
* The share this pane starts with. Panes with no `defaultSize` split whatever
|
|
41
|
+
* is left over equally.
|
|
42
|
+
*/
|
|
43
|
+
defaultSize?: PaneSize;
|
|
44
|
+
/** How small it may be dragged. @default 0 */
|
|
45
|
+
minSize?: PaneSize;
|
|
46
|
+
/** How large it may be dragged. Unbounded when left out. */
|
|
47
|
+
maxSize?: PaneSize;
|
|
48
|
+
/** What is inside the pane. */
|
|
49
|
+
children?: React.ReactNode;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A set of panes with draggable handles between them.
|
|
53
|
+
*
|
|
54
|
+
* The panes are sized in **fractions**, written out as
|
|
55
|
+
* `flex-basis: calc((100% - gutters) * fraction)`. That is the one decision the
|
|
56
|
+
* rest of this file follows from: a split described in percentages survives the
|
|
57
|
+
* window being resized without a single line of JavaScript running, so the
|
|
58
|
+
* component measures itself only twice β once on mount, to turn a `'240px'`
|
|
59
|
+
* default into a fraction, and once at the start of each drag, to know what a
|
|
60
|
+
* pixel of pointer movement is worth.
|
|
61
|
+
*
|
|
62
|
+
* The handles are interleaved here rather than written by the caller, so the
|
|
63
|
+
* children of a Panes are just panes. That does mean the direct children have to
|
|
64
|
+
* *be* Panes: the constraints are read off their props, and a Pane wrapped in
|
|
65
|
+
* something else is a pane with no minimum.
|
|
66
|
+
*/
|
|
67
|
+
export declare const Panes: React.ForwardRefExoticComponent<PanesProps & React.RefAttributes<HTMLDivElement>>;
|
|
68
|
+
/**
|
|
69
|
+
* One region of a split.
|
|
70
|
+
*
|
|
71
|
+
* It carries no surface of its own on purpose: a Panes is layout, and the moment
|
|
72
|
+
* a pane drew a sheet it would stop being usable as the thing a Card, a Table or
|
|
73
|
+
* an editor is put inside. Put a Box or a Card in it when a surface is wanted.
|
|
74
|
+
*
|
|
75
|
+
* `defaultSize`, `minSize` and `maxSize` are read by the Panes around it rather
|
|
76
|
+
* than used here β a pane cannot know what "half" is, only the split can.
|
|
77
|
+
*/
|
|
78
|
+
export declare const Pane: React.ForwardRefExoticComponent<PaneProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as n from"react";import{cx as r,transitionClasses as o}from"../../internal/styles";const i=n.createContext({basis:null}),a={xs:"basis-1",sm:"basis-1.5",md:"basis-2",lg:"basis-2.5",xl:"basis-3"},s={xs:4,sm:6,md:8,lg:10,xl:12};function l(e,t,n){if(null==e)return;if("number"==typeof e)return t*e/100;const r=/^\s*(-?[\d.]+)\s*(px|rem|em|%)\s*$/.exec(e);if(!r)return;const o=Number(r[1]);if(!Number.isNaN(o))switch(r[2]){case"px":return o;case"%":return t*o/100;case"rem":return o*parseFloat(getComputedStyle(document.documentElement).fontSize||"16");case"em":return o*parseFloat(n&&getComputedStyle(n).fontSize||"16");default:return}}export const Panes=n.forwardRef(function({orientation:c="horizontal",resizable:u=!0,color:d="primary",size:m="md",onResize:f,onResizeEnd:p,className:h,style:g,children:v,...x},b){const y=n.Children.toArray(v).filter(n.isValidElement),z=y.length,w=n.useRef(null),S=n.useCallback(e=>{w.current=e,"function"==typeof b?b(e):b&&(b.current=e)},[b]),R=n.useRef([]);R.current=y.map(e=>e.props);const[C,E]=n.useState(null),N=C&&C.length===z?C:null,M=n.useRef(null);M.current=N;const L="horizontal"===c,$=s[m]*Math.max(0,z-1);function k(e){const t=w.current,n=M.current;if(!u||!t||!n||void 0===n[e+1])return null;const r=t.getBoundingClientRect(),o=(L?r.width:r.height)-$;if(o<=0)return null;const i=R.current[e],a=R.current[e+1],s=n[e]*o,c=s+n[e+1]*o,d=Math.max(l(i?.minSize,o,t)??0,c-(l(a?.maxSize,o,t)??c)),m=Math.min(l(i?.maxSize,o,t)??c,c-(l(a?.minSize,o,t)??0));return m<d?null:{root:t,current:n,extent:o,start:s,pair:c,resize(t){const r=Math.min(m,Math.max(d,s+t)),i=[...n];return i[e]=r/o,i[e+1]=(c-r)/o,E(i),f?.(i.map(e=>100*e)),i}}}n.useEffect(()=>{const e=w.current;if(!e)return;const t=()=>{const t=e.getBoundingClientRect(),n=(L?t.width:t.height)-$;n<=0||E(t=>t&&t.length===z?t:function(e,t,n){const r=e.map(e=>l(e.defaultSize,t,n)),o=r.reduce((e,t)=>e+(t??0),0),i=r.filter(e=>void 0===e).length,a=i>0?Math.max(0,t-o)/i:0,s=r.map(e=>Math.max(0,e??a)),c=s.reduce((e,t)=>e+t,0);return c<=0?s.map(()=>1/Math.max(1,s.length)):s.map(e=>e/c)}(R.current,n,e))};t();const n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[z,L,$]);const A=r("group/handle relative z-1 flex shrink-0 grow-0 items-center justify-center",a[m],o,"[outline:none] focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-0",u?r(L?"cursor-col-resize":"cursor-row-resize","hover:bg-(--n-soft) data-[dragging]:bg-(--n-soft)"):"");return e("div",{ref:S,className:r("flex h-full w-full",L?"flex-row":"flex-col","min-h-0 min-w-0",h),style:{"--n-accent":`var(--neba-${d}-accent)`,"--n-soft":`var(--neba-${d}-soft)`,"--n-ring":`var(--neba-${d}-ring)`,...g},...x,children:y.map((a,s)=>t(n.Fragment,{children:[s>0?e("div",{role:"separator","aria-orientation":L?"vertical":"horizontal","aria-valuenow":N?Math.round(100*N[s-1]):void 0,"aria-valuemin":0,"aria-valuemax":100,"aria-disabled":!u||void 0,tabIndex:u?0:-1,className:A,onPointerDown:e=>{0===e.button&&function(e,t){const n=k(e);if(!n)return;const r=t.currentTarget;r.setPointerCapture(t.pointerId),r.dataset.dragging="true";const o=document.body.style.userSelect;document.body.style.userSelect="none";const i=L?t.clientX:t.clientY,a=L&&"rtl"===getComputedStyle(n.root).direction?-1:1;let s=n.current;const l=e=>{const t=L?e.clientX:e.clientY;s=n.resize((t-i)*a)},c=()=>{r.removeEventListener("pointermove",l),r.removeEventListener("pointerup",c),r.removeEventListener("pointercancel",c),delete r.dataset.dragging,document.body.style.userSelect=o,p?.(s.map(e=>100*e))};r.addEventListener("pointermove",l),r.addEventListener("pointerup",c),r.addEventListener("pointercancel",c)}(s-1,e)},onKeyDown:e=>{const t=L?"ArrowLeft":"ArrowUp",n=L?"ArrowRight":"ArrowDown";e.key!==t&&e.key!==n||(e.preventDefault(),function(e,t){const n=k(e);if(!n)return;const r=n.resize(t);p?.(r.map(e=>100*e))}(s-1,e.key===n?16:-16))},children:e("span",{"aria-hidden":"true",className:r("pointer-events-none bg-(--neba-border)",o,L?"h-full w-px":"h-px w-full",u?"group-hover/handle:bg-(--n-accent) group-focus-visible/handle:bg-(--n-accent) group-data-[dragging]/handle:bg-(--n-accent)":"")})}):null,e(i.Provider,{value:{basis:N?`calc((100% - ${$}px) * ${N[s].toFixed(6)})`:null},children:a})]},a.key??s))})});export const Pane=n.forwardRef(function({defaultSize:t,minSize:o,maxSize:a,className:s,style:l,children:c,...u},d){const{basis:m}=n.useContext(i);return e("div",{ref:d,className:r("relative min-h-0 min-w-0 overflow-auto",s),style:{flex:m?`0 0 ${m}`:"1 1 0%",...l},...u,children:c})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Pane,Panes}from"./Panes";
|