neba 1.1.0 → 1.3.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 +12 -7
- package/dist/components/alert/Alert.d.ts +7 -1
- package/dist/components/alert/Alert.js +1 -1
- package/dist/components/animate-appear/AnimateAppear.d.ts +55 -0
- package/dist/components/animate-appear/AnimateAppear.js +1 -0
- package/dist/components/animate-appear/index.d.ts +2 -0
- package/dist/components/animate-appear/index.js +1 -0
- package/dist/components/animate-blink/AnimateBlink.d.ts +31 -0
- package/dist/components/animate-blink/AnimateBlink.js +1 -0
- package/dist/components/animate-blink/index.d.ts +2 -0
- package/dist/components/animate-blink/index.js +1 -0
- package/dist/components/animate-fade/AnimateFade.d.ts +31 -0
- package/dist/components/animate-fade/AnimateFade.js +1 -0
- package/dist/components/animate-fade/index.d.ts +2 -0
- package/dist/components/animate-fade/index.js +1 -0
- package/dist/components/animate-grow/AnimateGrow.d.ts +41 -0
- package/dist/components/animate-grow/AnimateGrow.js +1 -0
- package/dist/components/animate-grow/index.d.ts +2 -0
- package/dist/components/animate-grow/index.js +1 -0
- package/dist/components/animate-headline/AnimateHeadline.d.ts +55 -0
- package/dist/components/animate-headline/AnimateHeadline.js +1 -0
- package/dist/components/animate-headline/index.d.ts +2 -0
- package/dist/components/animate-headline/index.js +1 -0
- package/dist/components/animate-lighting/AnimateLighting.d.ts +62 -0
- package/dist/components/animate-lighting/AnimateLighting.js +1 -0
- package/dist/components/animate-lighting/index.d.ts +2 -0
- package/dist/components/animate-lighting/index.js +1 -0
- package/dist/components/animate-marquee/AnimateMarquee.d.ts +65 -0
- package/dist/components/animate-marquee/AnimateMarquee.js +1 -0
- package/dist/components/animate-marquee/index.d.ts +2 -0
- package/dist/components/animate-marquee/index.js +1 -0
- package/dist/components/animate-rotate/AnimateRotate.d.ts +48 -0
- package/dist/components/animate-rotate/AnimateRotate.js +1 -0
- package/dist/components/animate-rotate/index.d.ts +2 -0
- package/dist/components/animate-rotate/index.js +1 -0
- package/dist/components/animate-slide/AnimateSlide.d.ts +46 -0
- package/dist/components/animate-slide/AnimateSlide.js +1 -0
- package/dist/components/animate-slide/index.d.ts +2 -0
- package/dist/components/animate-slide/index.js +1 -0
- package/dist/components/animate-typing/AnimateTyping.d.ts +58 -0
- package/dist/components/animate-typing/AnimateTyping.js +1 -0
- package/dist/components/animate-typing/index.d.ts +2 -0
- package/dist/components/animate-typing/index.js +1 -0
- package/dist/components/animate-zoom/AnimateZoom.d.ts +37 -0
- package/dist/components/animate-zoom/AnimateZoom.js +1 -0
- package/dist/components/animate-zoom/index.d.ts +2 -0
- package/dist/components/animate-zoom/index.js +1 -0
- package/dist/components/aspect-ratio/AspectRatio.d.ts +63 -0
- package/dist/components/aspect-ratio/AspectRatio.js +1 -0
- package/dist/components/aspect-ratio/index.d.ts +2 -0
- package/dist/components/aspect-ratio/index.js +1 -0
- package/dist/components/avatar/Avatar.d.ts +106 -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 +7 -1
- package/dist/components/blockquote/Blockquote.js +1 -1
- package/dist/components/box/Box.d.ts +7 -1
- package/dist/components/box/Box.js +1 -1
- 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/chip/Chip.d.ts +7 -1
- package/dist/components/chip/Chip.js +1 -1
- package/dist/components/color-picker/ColorPicker.d.ts +102 -0
- package/dist/components/color-picker/ColorPicker.js +1 -0
- package/dist/components/color-picker/index.d.ts +2 -0
- package/dist/components/color-picker/index.js +1 -0
- package/dist/components/drawer/Drawer.d.ts +145 -0
- package/dist/components/drawer/Drawer.js +1 -0
- package/dist/components/drawer/index.d.ts +2 -0
- package/dist/components/drawer/index.js +1 -0
- package/dist/components/icon/Icon.d.ts +8 -1
- package/dist/components/icon/Icon.js +1 -1
- 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/popover/Popover.d.ts +109 -0
- package/dist/components/popover/Popover.js +1 -0
- package/dist/components/popover/index.d.ts +2 -0
- package/dist/components/popover/index.js +1 -0
- package/dist/components/skeleton/Skeleton.d.ts +76 -0
- package/dist/components/skeleton/Skeleton.js +1 -0
- package/dist/components/skeleton/index.d.ts +2 -0
- package/dist/components/skeleton/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/components/typography/Typography.d.ts +7 -1
- package/dist/components/typography/Typography.js +1 -1
- package/dist/index.d.ts +24 -0
- package/dist/index.js +1 -1
- package/dist/internal/animate.d.ts +175 -0
- package/dist/internal/animate.js +1 -0
- package/dist/internal/color.d.ts +108 -0
- package/dist/internal/color.js +1 -0
- package/dist/internal/i18n.d.ts +103 -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 +504 -0
- package/dist/types.d.ts +112 -0
- package/package.json +14 -14
|
@@ -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";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps, NebaTransition } from '../../types';
|
|
3
3
|
export interface ChipProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'span'>, 'color'> {
|
|
4
4
|
/**
|
|
5
5
|
* Drop shadow depth. `0` (the default) is flat. A chip is a token sitting on
|
|
@@ -7,6 +7,12 @@ export interface ChipProps extends NebaStyleProps, Omit<React.ComponentPropsWith
|
|
|
7
7
|
* @default 0
|
|
8
8
|
*/
|
|
9
9
|
elevation?: NebaElevation;
|
|
10
|
+
/**
|
|
11
|
+
* An entrance animation, run once on mount: `transition="fade"`, or an object
|
|
12
|
+
* for the details. For a trigger, a replay or anything under your own
|
|
13
|
+
* control, wrap it in an `Animate*` component instead.
|
|
14
|
+
*/
|
|
15
|
+
transition?: NebaTransition;
|
|
10
16
|
/** Content placed before the label — an icon, a status dot, an avatar. */
|
|
11
17
|
startIcon?: React.ReactNode;
|
|
12
18
|
/** Content placed after the label, before any `count`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,Fragment as n,jsxs as
|
|
1
|
+
import{jsx as e,Fragment as n,jsxs as t}from"react/jsx-runtime";import*as l from"react";import{CloseIcon as o}from"../../internal/icons";import{transitionProps as i}from"../../internal/animate";import{chipRemoveClasses as r,controlHeightClasses as a,controlSlots as s,controlTextClasses as c,disabledClasses as d,focusRingClasses as m,gapClasses as p,iconClasses as b,paddingXClasses as f,pressTransitionClasses as u,radiusClasses as h,surfaceClasses as v,transitionClasses as x}from"../../internal/styles";const g={xs:"xs",sm:"xs",md:"sm",lg:"md",xl:"lg"},j={solid:[v,"text-(--n-on-solid) bg-(--n-fill)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[v,"border text-(--n-accent) bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:"text-(--n-accent) bg-(--n-soft)"},y={solid:"bg-(--n-fill-active)",outline:"bg-(--n-panel-press) [border-color:var(--n-line-hover)]",text:"bg-(--n-soft-press)"},w={solid:"hover:bg-(--n-fill-hover) active:bg-(--n-fill-active)",outline:"hover:bg-(--n-panel-hover) hover:[border-color:var(--n-line-hover)] active:bg-(--n-panel-press)",text:"hover:bg-(--n-soft-hover) active:bg-(--n-soft-press)"},N=["relative inline-flex max-w-full shrink-0 select-none items-center","align-middle font-medium leading-none whitespace-nowrap","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",x,u,b].join(" "),k=["flex min-w-0 flex-1 cursor-pointer items-center justify-center self-stretch rounded-[inherit]",m].join(" ");export const Chip=l.forwardRef(function({variant:l="outline",size:m="md",color:b="primary",density:u="default",elevation:v=0,startIcon:x,endIcon:C,count:$,onDelete:B,deleteLabel:I="Remove",transition:R,selected:z=!1,disabled:D=!1,className:F,style:L,children:q,onClick:A,...E},G){const H=Boolean(A)&&!D,J=g[m],K=i(R),M=f[u][J],O=[N,a[J],c[J],p[J],h[J],D?d[l]:j[l],!D&&z?y[l]:"",H?w[l]:"",H?"ps-0":M,B?"pe-1":H?"pe-0":"",K.className,F??""].filter(Boolean).join(" "),P=t(n,{children:[x,null!=q&&!1!==q?e("span",{className:"min-w-0 truncate",children:q}):null,C,null!=$&&!1!==$?e("span",{className:["ms-0.5 inline-flex shrink-0 items-center justify-center rounded-full px-1.5 py-px","text-[0.85em] leading-none font-semibold tabular-nums","solid"===l?"bg-(--neba-glow-on-fill) text-(--n-on-solid)":"bg-(--n-soft-press) text-(--n-accent)"].join(" "),children:$}):null]});return t("span",{ref:G,className:O,style:{...s(b,v,l),...K.style,...L},"aria-disabled":!(!D||H)||void 0,...E,children:[H?e("button",{type:"button","aria-pressed":z,className:`${k} ${p[J]} ${M}`,onClick:A,children:P}):P,B?e("button",{type:"button","aria-label":I,disabled:D,className:r,onClick:B,children:e(o,{})}):null]})});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ColorFormat } from '../../internal/color';
|
|
3
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
4
|
+
/** The names for the parts of the picker that have no text on them. */
|
|
5
|
+
export interface ColorPickerLabels {
|
|
6
|
+
/** The saturation/brightness square. */
|
|
7
|
+
area: string;
|
|
8
|
+
/** The hue rail beside it. */
|
|
9
|
+
hue: string;
|
|
10
|
+
/** The opacity rail, when `alpha` is on. */
|
|
11
|
+
alpha: string;
|
|
12
|
+
/** The field the value can be typed into. */
|
|
13
|
+
value: string;
|
|
14
|
+
/** The grid of ready-made colours. */
|
|
15
|
+
swatches: string;
|
|
16
|
+
/** The × that empties the control. */
|
|
17
|
+
clear: string;
|
|
18
|
+
/** What the trigger reads before anything has been chosen. */
|
|
19
|
+
empty: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ColorPickerProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'defaultValue' | 'onChange'> {
|
|
22
|
+
/** The colour, as a CSS string. Pass it to drive the picker yourself. */
|
|
23
|
+
value?: string;
|
|
24
|
+
/** Where an uncontrolled picker starts. @default '#1a58d1' */
|
|
25
|
+
defaultValue?: string;
|
|
26
|
+
/** Called with the new colour, written in `format`. */
|
|
27
|
+
onValueChange?: (value: string) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Which notation the value is written in on the way out.
|
|
30
|
+
* @default 'hex'
|
|
31
|
+
*/
|
|
32
|
+
format?: ColorFormat;
|
|
33
|
+
/**
|
|
34
|
+
* Offers an opacity rail, and lets the value carry a fourth channel.
|
|
35
|
+
* @default false
|
|
36
|
+
*/
|
|
37
|
+
alpha?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* The ready-made colours under the panel. `false` draws none; an array of CSS
|
|
40
|
+
* colour strings replaces the built-in set.
|
|
41
|
+
*/
|
|
42
|
+
swatches?: readonly string[] | false;
|
|
43
|
+
/**
|
|
44
|
+
* Draws the panel in the page instead of in a popup, with no trigger.
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
inline?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* The field under the panel that the value can be typed into.
|
|
50
|
+
* @default true
|
|
51
|
+
*/
|
|
52
|
+
editable?: boolean;
|
|
53
|
+
/** Label above the control. */
|
|
54
|
+
label?: React.ReactNode;
|
|
55
|
+
/** Helper text below it. */
|
|
56
|
+
description?: React.ReactNode;
|
|
57
|
+
/** Error message below. Its presence also turns the control invalid. */
|
|
58
|
+
error?: React.ReactNode;
|
|
59
|
+
invalid?: boolean;
|
|
60
|
+
required?: boolean;
|
|
61
|
+
disabled?: boolean;
|
|
62
|
+
/** Shows the colour and forbids changing it. @default false */
|
|
63
|
+
readOnly?: boolean;
|
|
64
|
+
/** Stretches the trigger to its container. @default false */
|
|
65
|
+
fullWidth?: boolean;
|
|
66
|
+
/** Offers the × that empties the control. @default false */
|
|
67
|
+
clearable?: boolean;
|
|
68
|
+
/** Submits with a form under this name. */
|
|
69
|
+
name?: string;
|
|
70
|
+
/** Whether the popup is open. Pass it to drive the popup yourself. */
|
|
71
|
+
open?: boolean;
|
|
72
|
+
/** Where an uncontrolled popup starts. @default false */
|
|
73
|
+
defaultOpen?: boolean;
|
|
74
|
+
onOpenChange?: (open: boolean) => void;
|
|
75
|
+
/**
|
|
76
|
+
* Which language the accessible names are written in — a BCP 47 tag such as
|
|
77
|
+
* `ko`, `pt-BR` or `zh-Hant`. Unsupported tags fall back to English.
|
|
78
|
+
*/
|
|
79
|
+
locale?: string;
|
|
80
|
+
/** Overrides for those names, one at a time. */
|
|
81
|
+
labels?: Partial<ColorPickerLabels>;
|
|
82
|
+
/** Drop shadow depth on the trigger. `0` (the default) is flat. @default 0 */
|
|
83
|
+
elevation?: NebaElevation;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A colour, chosen by eye.
|
|
87
|
+
*
|
|
88
|
+
* A saturation square with a hue rail beside it — the arrangement every design
|
|
89
|
+
* tool has settled on, because it is the one that puts every colour of a hue
|
|
90
|
+
* within a single movement of the pointer. `alpha` adds a third rail, `format`
|
|
91
|
+
* decides which notation comes back out, and `swatches` puts the handful of
|
|
92
|
+
* colours a product actually uses one click away.
|
|
93
|
+
*
|
|
94
|
+
* The panel's state is HSV and it never leaves that model, which is what keeps
|
|
95
|
+
* the hue rail still while the pointer is in the black corner: through RGB,
|
|
96
|
+
* every shade of black is the same colour and the rail would snap to red.
|
|
97
|
+
*
|
|
98
|
+
* There is no colour library under this. The conversions are in
|
|
99
|
+
* `internal/color.ts`, which is a hundred lines of arithmetic — the whole
|
|
100
|
+
* reason the package still has one runtime dependency.
|
|
101
|
+
*/
|
|
102
|
+
export declare const ColorPicker: React.ForwardRefExoticComponent<ColorPickerProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as r from"react";import{defaultPickerLabels as l}from"../../internal/calendar";import{PickerShell as n}from"../../internal/picker";import{CheckIcon as t}from"../../internal/icons";import{useMessages as o}from"../../internal/i18n";import{checkerBackground as s,clamp as i,cssColor as d,defaultSwatches as h,formatColor as c,parseColor as u,readableInk as f}from"../../internal/color";import{controlTextClasses as p,cx as b,focusRingClasses as m,metaTextClasses as g,radiusClasses as v,stackGapClasses as x,surfaceSlots as y}from"../../internal/styles";const w={xs:"w-40",sm:"w-44",md:"w-52",lg:"w-60",xl:"w-72"},N={xs:"h-24",sm:"h-28",md:"h-32",lg:"h-40",xl:"h-48"},k={xs:"h-2.5",sm:"h-3",md:"h-3.5",lg:"h-4",xl:"h-5"},C={xs:"gap-1.5",sm:"gap-2",md:"gap-2.5",lg:"gap-3",xl:"gap-3.5"},_={xs:10,sm:12,md:14,lg:16,xl:18},z=["relative overflow-hidden border","[border-color:var(--neba-border)]","[box-shadow:var(--neba-plate-glass)]"].join(" "),I=["pointer-events-none absolute rounded-full border-2 border-white","[box-shadow:0_0_0_1px_rgba(0,0,0,0.35),0_1px_3px_rgba(0,0,0,0.4)]"].join(" ");function T(e){const a=e.currentTarget.getBoundingClientRect();return{x:0===a.width?0:i((e.clientX-a.left)/a.width,0,1),y:0===a.height?0:i((e.clientY-a.top)/a.height,0,1)}}function j(e){const a=e.shiftKey?10:1;switch(e.key){case"ArrowLeft":return{x:-a,y:0};case"ArrowRight":return{x:a,y:0};case"ArrowUp":return{x:0,y:a};case"ArrowDown":return{x:0,y:-a};default:return null}}function A({hsv:r,alphaValue:l,onChange:n,text:o,onTextChange:h,withAlpha:p,swatches:x,editable:y,size:A,inert:D,labels:M}){const O=_[A],P=-O/2,V=d({h:r.h,s:100,v:100}),$=d(r),L=e=>({onPointerDown:a=>{D||(a.currentTarget.setPointerCapture(a.pointerId),e(a))},onPointerMove:a=>{!D&&a.currentTarget.hasPointerCapture(a.pointerId)&&e(a)}}),S=(e,a,r,l)=>({role:"slider",tabIndex:D?-1:0,"aria-label":e,"aria-valuemin":0,"aria-valuemax":r,"aria-valuenow":Math.round(a),"aria-orientation":"horizontal","aria-disabled":D||void 0,onKeyDown:e=>{const a=j(e);!D&&a&&0!==a.x&&(e.preventDefault(),l(a.x))}});return a("div",{className:b("flex flex-col",w[A],C[A]),children:[e("div",{...L(e=>{const{x:a,y:t}=T(e);n({hsv:{h:r.h,s:100*a,v:100*(1-t)},alpha:l})}),role:"slider",tabIndex:D?-1:0,"aria-label":M.area,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":Math.round(r.s),"aria-valuetext":`${Math.round(r.s)}%, ${Math.round(r.v)}%`,"aria-disabled":D||void 0,onKeyDown:e=>{const a=j(e);!D&&a&&(e.preventDefault(),n({hsv:{h:r.h,s:i(r.s+a.x,0,100),v:i(r.v+a.y,0,100)},alpha:l}))},className:b(z,N[A],v[A],m,D?"cursor-default":"cursor-crosshair touch-none"),style:{backgroundColor:V,backgroundImage:"linear-gradient(to top, #000000, rgba(0, 0, 0, 0)), linear-gradient(to right, #ffffff, rgba(255, 255, 255, 0))"},children:e("span",{className:I,style:{width:O,height:O,left:`${r.s}%`,top:100-r.v+"%",marginLeft:P,marginTop:P,backgroundColor:$}})}),e("div",{...L(e=>n({hsv:{...r,h:360*T(e).x},alpha:l})),...S(M.hue,r.h,360,e=>n({hsv:{...r,h:(r.h+2*e+360)%360},alpha:l})),className:b(z,k[A],"rounded-full",m,D?"cursor-default":"cursor-pointer touch-none"),style:{backgroundImage:"linear-gradient(to right, #ff0000 0%, #ffff00 16.66%, #00ff00 33.33%, #00ffff 50%, #0000ff 66.66%, #ff00ff 83.33%, #ff0000 100%)"},children:e("span",{className:I,style:{width:O,height:O,left:r.h/360*100+"%",top:"50%",marginLeft:P,marginTop:P,backgroundColor:V}})}),p?a("div",{...L(e=>n({hsv:r,alpha:T(e).x})),...S(M.alpha,100*l,100,e=>n({hsv:r,alpha:i(l+e/100,0,1)})),className:b(z,k[A],"rounded-full",m,D?"cursor-default":"cursor-pointer touch-none"),style:s,children:[e("span",{"aria-hidden":"true",className:"absolute inset-0",style:{backgroundImage:`linear-gradient(to right, ${d(r,0)}, ${$})`}}),e("span",{className:I,style:{width:O,height:O,left:100*l+"%",top:"50%",marginLeft:P,marginTop:P,backgroundColor:d(r,l)}})]}):null,y?a("div",{className:b("flex items-center",C[A]),children:[e("span",{"aria-hidden":"true",className:b("shrink-0 rounded-full border","[border-color:var(--neba-border)]"),style:{width:O+6,height:O+6,...s},children:e("span",{className:"block size-full rounded-full",style:{backgroundColor:d(r,l)}})}),e("input",{type:"text",value:o,readOnly:D,spellCheck:!1,autoComplete:"off","aria-label":M.value,onChange:e=>h(e.target.value),className:b("min-w-0 flex-1 border bg-transparent px-1.5 py-1 font-mono lowercase","[border-color:var(--neba-border)]",v.xs,g[A],"text-(--neba-fg)","[outline:none] focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-1")})]}):null,x&&x.length>0?e("div",{role:"group","aria-label":M.swatches,className:"grid grid-cols-8 gap-1",children:x.map(a=>{const o=u(a),s=null!==o&&c(o.hsv,o.alpha,"hex")===c(r,l,"hex");return e("button",{type:"button",disabled:D,"aria-label":a,"aria-pressed":s,onClick:()=>{o&&n(o)},className:b("flex aspect-square items-center justify-center rounded-full border","[border-color:color-mix(in_oklab,var(--neba-fg)_18%,transparent)]","[transition:box-shadow_var(--neba-duration)_var(--neba-ease)]",m,"[outline:none]",D?"cursor-default":"cursor-pointer hover:[box-shadow:var(--neba-shadow-1)]","[&_svg]:size-3"),style:{backgroundColor:a},children:s&&o?e("span",{style:{color:f(o.hsv)},children:e(t,{})}):null},a)})}):null]})}function D(e,a){return"hex"===e?[a?"#ffffffff":"#ffffff"]:"rgb"===e?[a?"rgba(255, 255, 255, 0.55)":"rgb(255, 255, 255)"]:[a?"hsla(360, 100%, 100%, 0.55)":"hsl(360, 100%, 100%)"]}const M={h:217,s:87,v:82};export const ColorPicker=r.forwardRef(function({value:t,defaultValue:i="#1a58d1",onValueChange:f,format:m="hex",alpha:v=!1,swatches:w=h,inline:N=!1,editable:k=!0,label:C,description:_,error:z,invalid:I,required:T=!1,disabled:j=!1,readOnly:O=!1,fullWidth:P=!1,clearable:V=!1,name:$,open:L,defaultOpen:S=!1,onOpenChange:q,locale:K,labels:R,variant:B="outline",size:W="md",color:E="primary",density:U="default",elevation:X=0,className:Y,style:F,...G},H){const J=o(K),Q=r.useMemo(()=>({...J.color,...R}),[J,R]),[Z,ee]=r.useState(i),ae=t??Z,[re,le]=r.useState(S),ne=L??re,[te,oe]=r.useState(()=>u(ae)??{hsv:M,alpha:1}),[se,ie]=r.useState(()=>ae||""),de=c(te.hsv,v?te.alpha:1,m),he=""===ae;r.useEffect(()=>{const e=u(ae);e?c(e.hsv,v?e.alpha:1,m)!==de&&(ie(ae),oe(e)):ie(ae)},[ae,de,v,m]);const ce=(e,a)=>{oe(e);const r=c(e.hsv,v?e.alpha:1,m);ie(a??r),void 0===t&&ee(r),f?.(r)},ue=j||O,fe=e(A,{hsv:te.hsv,alphaValue:te.alpha,onChange:ce,text:se,onTextChange:e=>{ie(e);const a=u(e);a&&ce(a,e)},withAlpha:v,swatches:w,editable:k,size:W,inert:ue,labels:Q}),pe=$?e("input",{type:"hidden",name:$,value:he?"":de}):null;if(N){const r=I??Boolean(z)?"danger":E;return a("div",{ref:H,className:b("flex flex-col",x[W],Y),style:{...y(r,X),...F},...G,children:[C?e("span",{className:b(g[W],"font-medium",j?"text-(--neba-disabled-fg)":"text-(--neba-fg)"),children:C}):null,fe,_?e("span",{className:b(g[W],"text-(--neba-muted-fg)"),children:_}):null,z?e("span",{className:b(g[W],"text-(--n-accent)"),children:z}):null,pe]})}return e("div",{ref:H,className:P?"w-full":"inline-block",...G,children:e(n,{variant:B,size:W,color:E,density:U,elevation:X,style:F,label:C,description:_,error:z,invalid:I,required:T,disabled:j,readOnly:O,fullWidth:P,className:Y,startIcon:e("span",{"aria-hidden":"true",className:b("block size-[1.15em] shrink-0 rounded-full border","[border-color:color-mix(in_oklab,var(--neba-fg)_18%,transparent)]"),style:s,children:e("span",{className:"block size-full rounded-full",style:{backgroundColor:he?"transparent":d(te.hsv,v?te.alpha:1)}})}),display:he?Q.empty:de,samples:D(m,v),empty:he,clearable:V,onClear:()=>{void 0===t&&ee(""),ie(""),f?.("")},open:ne,onOpenChange:e=>{void 0===L&&le(e),q?.(e)},labels:{...l,clear:Q.clear},hiddenValues:$?[{name:$,value:he?"":de}]:void 0,children:e("div",{className:p[W],children:fe})})})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{ColorPicker}from"./ColorPicker";
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Dialog as BaseUIDialog } from '@base-ui/react/dialog';
|
|
3
|
+
import type { NebaSide, NebaStyleProps } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* How the panel relates to the page.
|
|
6
|
+
*
|
|
7
|
+
* - `overlay` — it is opened, it floats over the page on a scrim, it holds the
|
|
8
|
+
* focus, and it is dismissed. The navigation drawer behind a hamburger, the
|
|
9
|
+
* filter panel beside a table.
|
|
10
|
+
* - `inline` — it is part of the layout and the page is laid out around it. No
|
|
11
|
+
* scrim, no portal, no focus trap, nothing to dismiss. The sidebar that is
|
|
12
|
+
* simply there.
|
|
13
|
+
*
|
|
14
|
+
* A separate axis from `variant`, which already means the weight of a surface
|
|
15
|
+
* across the whole library and would be a second spelling of nothing.
|
|
16
|
+
*/
|
|
17
|
+
export type NebaDrawerMode = 'overlay' | 'inline';
|
|
18
|
+
/**
|
|
19
|
+
* A drawer takes `size`, `color` and `density` and stops there.
|
|
20
|
+
*
|
|
21
|
+
* There is no `variant`, for Dialog's reason: the three weights answer "how
|
|
22
|
+
* much does this surface assert itself against the page", and a panel that has
|
|
23
|
+
* taken an edge of the window has answered it. There is no `elevation` either —
|
|
24
|
+
* an `overlay` drawer floats and carries a shadow at level 3, an `inline` one is
|
|
25
|
+
* part of the layout and carries none, and neither is a decision worth offering.
|
|
26
|
+
*/
|
|
27
|
+
export interface DrawerProps extends Pick<NebaStyleProps, 'size' | 'color' | 'density'> {
|
|
28
|
+
/**
|
|
29
|
+
* Which edge the panel is attached to. Physical rather than logical, the way
|
|
30
|
+
* `NebaSide` is everywhere: a drawer along the top of the window is along the
|
|
31
|
+
* top in every writing direction.
|
|
32
|
+
* @default 'left'
|
|
33
|
+
*/
|
|
34
|
+
side?: NebaSide;
|
|
35
|
+
/** @default 'overlay' */
|
|
36
|
+
mode?: NebaDrawerMode;
|
|
37
|
+
/** The drawer is shown. Use with `onOpenChange` for a controlled drawer. */
|
|
38
|
+
open?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Whether the drawer starts open, for an uncontrolled one.
|
|
41
|
+
*
|
|
42
|
+
* Defaults to `false` in `overlay` mode and `true` in `inline` mode, because
|
|
43
|
+
* a fixed sidebar that had to be opened before it appeared would not be a
|
|
44
|
+
* fixed sidebar.
|
|
45
|
+
*/
|
|
46
|
+
defaultOpen?: boolean;
|
|
47
|
+
onOpenChange?: (open: boolean) => void;
|
|
48
|
+
/**
|
|
49
|
+
* The element that opens the drawer, wired up by Base UI.
|
|
50
|
+
*
|
|
51
|
+
* `overlay` only. An `inline` drawer is not opened — it is in the layout —
|
|
52
|
+
* so a trigger there would have nothing to do and is not rendered.
|
|
53
|
+
*/
|
|
54
|
+
trigger?: React.ReactElement;
|
|
55
|
+
/** The heading. Rendered as the element that names the drawer. */
|
|
56
|
+
title?: React.ReactNode;
|
|
57
|
+
/** A line under the title, and the drawer's accessible description. */
|
|
58
|
+
description?: React.ReactNode;
|
|
59
|
+
/**
|
|
60
|
+
* The bottom row, held against the foot of the panel while the body scrolls.
|
|
61
|
+
* Laid out end-aligned, so a pair of buttons needs no wrapper of its own —
|
|
62
|
+
* and `DrawerClose` is what makes one of them dismiss.
|
|
63
|
+
*/
|
|
64
|
+
actions?: React.ReactNode;
|
|
65
|
+
/**
|
|
66
|
+
* Draws a hairline between the header, the body and the actions instead of
|
|
67
|
+
* separating them with space. Worth turning on the moment the body scrolls:
|
|
68
|
+
* the lines are what say the header stayed put.
|
|
69
|
+
* @default false
|
|
70
|
+
*/
|
|
71
|
+
dividers?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Shows the × in the corner. On in `overlay` mode, where the panel has taken
|
|
74
|
+
* the page and the way out should not have to be remembered; off in `inline`
|
|
75
|
+
* mode, where a × that closes a fixed sidebar with nothing to reopen it is a
|
|
76
|
+
* one-way door.
|
|
77
|
+
*/
|
|
78
|
+
showClose?: boolean;
|
|
79
|
+
/** Accessible name of the × button. */
|
|
80
|
+
closeLabel?: string;
|
|
81
|
+
/**
|
|
82
|
+
* How far the panel reaches in from its edge: a **width** for `left` and
|
|
83
|
+
* `right`, a **height** for `top` and `bottom`. Numbers are pixels.
|
|
84
|
+
*
|
|
85
|
+
* Left alone, a side panel takes the width its `size` implies and a top or
|
|
86
|
+
* bottom panel is as tall as what is in it, up to 85% of the window.
|
|
87
|
+
*/
|
|
88
|
+
extent?: number | string;
|
|
89
|
+
/**
|
|
90
|
+
* Rounds the two corners on the edge that faces the page — the top and bottom
|
|
91
|
+
* of a side panel, the inner pair of a top or bottom one. The corners against
|
|
92
|
+
* the window edge are always square, because a corner cut off something that
|
|
93
|
+
* has no visible end is a corner cut off nothing.
|
|
94
|
+
* @default true
|
|
95
|
+
*/
|
|
96
|
+
rounded?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Whether the page behind is taken away. `'trap-focus'` keeps the page
|
|
99
|
+
* scrollable and clickable while still holding focus inside. `overlay` only.
|
|
100
|
+
* @default true
|
|
101
|
+
*/
|
|
102
|
+
modal?: boolean | 'trap-focus';
|
|
103
|
+
/**
|
|
104
|
+
* Whether pressing Escape or clicking the scrim closes the drawer. Turn it off
|
|
105
|
+
* for the drawer that has to be answered — and then give it actions that
|
|
106
|
+
* answer it, because there will be no other way out. `overlay` only.
|
|
107
|
+
* @default true
|
|
108
|
+
*/
|
|
109
|
+
dismissible?: boolean;
|
|
110
|
+
/** The body. */
|
|
111
|
+
children?: React.ReactNode;
|
|
112
|
+
className?: string;
|
|
113
|
+
style?: React.CSSProperties;
|
|
114
|
+
}
|
|
115
|
+
export type DrawerCloseProps = React.ComponentPropsWithoutRef<typeof BaseUIDialog.Close>;
|
|
116
|
+
/**
|
|
117
|
+
* Closes the drawer it is inside.
|
|
118
|
+
*
|
|
119
|
+
* Exported for `DialogClose`'s reason: an uncontrolled drawer has no `setOpen`
|
|
120
|
+
* for its Cancel button to call. It is an `overlay` drawer's button — an
|
|
121
|
+
* `inline` drawer is not a Base UI dialog and has nothing for this to talk to.
|
|
122
|
+
*
|
|
123
|
+
* `render` is Base UI's own escape hatch, so a real Neba button dismisses:
|
|
124
|
+
* `<DrawerClose render={<Button variant="text">Cancel</Button>} />`.
|
|
125
|
+
*/
|
|
126
|
+
export declare const DrawerClose: React.ForwardRefExoticComponent<Omit<import("@base-ui/react").AlertDialogCloseProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
127
|
+
/**
|
|
128
|
+
* A panel attached to one edge of the window.
|
|
129
|
+
*
|
|
130
|
+
* Two things in one component, because they are the same panel: `overlay` is the
|
|
131
|
+
* drawer you open — a scrim, a focus trap, Escape — and `inline` is the drawer
|
|
132
|
+
* that is simply part of the page. Everything else about them is identical,
|
|
133
|
+
* which is exactly why they should not be two components a caller has to switch
|
|
134
|
+
* between when a sidebar becomes a hamburger at a breakpoint.
|
|
135
|
+
*
|
|
136
|
+
* The sections are props rather than compound sub-components, as on Card and
|
|
137
|
+
* Dialog: the arrangement is fixed — heading, description, body, actions — and
|
|
138
|
+
* what a caller wants to decide is what goes in each slot. The body is the only
|
|
139
|
+
* part that scrolls, so the heading and the actions stay put.
|
|
140
|
+
*
|
|
141
|
+
* In `overlay` mode Base UI owns everything hard about it: the focus trap, the
|
|
142
|
+
* scroll lock, the `aria-labelledby` / `aria-describedby` wiring, restoring
|
|
143
|
+
* focus to the trigger, and the inert page behind.
|
|
144
|
+
*/
|
|
145
|
+
export declare function Drawer({ side, mode, size, color, density, open, defaultOpen, onOpenChange, trigger, title, description, actions, dividers, showClose, closeLabel, extent, rounded, modal, dismissible, className, style, children }: DrawerProps): React.JSX.Element | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as r,Fragment as a}from"react/jsx-runtime";import{Dialog as l}from"@base-ui/react/dialog";import{boxPaddingXClasses as n,boxPaddingYClasses as o}from"../box/Box";import{CloseIcon as s}from"../../internal/icons";import{hasContent as i,metaTextClasses as d,sheetBodyClasses as t,sheetHeaderGapClasses as u,sheetSectionGapClasses as b,sheetTitleClasses as m,surfaceClasses as c,surfaceSlots as f}from"../../internal/styles";const x={xs:"w-56",sm:"w-64",md:"w-80",lg:"w-96",xl:"w-[28rem]"},p={left:{xs:"rounded-r-(--neba-radius-xs)",sm:"rounded-r-(--neba-radius-sm)",md:"rounded-r-(--neba-radius-md)",lg:"rounded-r-(--neba-radius-lg)",xl:"rounded-r-(--neba-radius-xl)"},right:{xs:"rounded-l-(--neba-radius-xs)",sm:"rounded-l-(--neba-radius-sm)",md:"rounded-l-(--neba-radius-md)",lg:"rounded-l-(--neba-radius-lg)",xl:"rounded-l-(--neba-radius-xl)"},top:{xs:"rounded-b-(--neba-radius-xs)",sm:"rounded-b-(--neba-radius-sm)",md:"rounded-b-(--neba-radius-md)",lg:"rounded-b-(--neba-radius-lg)",xl:"rounded-b-(--neba-radius-xl)"},bottom:{xs:"rounded-t-(--neba-radius-xs)",sm:"rounded-t-(--neba-radius-sm)",md:"rounded-t-(--neba-radius-md)",lg:"rounded-t-(--neba-radius-lg)",xl:"rounded-t-(--neba-radius-xl)"}},g={left:"border-r",right:"border-l",top:"border-b",bottom:"border-t"},h={left:"flex-row justify-start",right:"flex-row justify-end",top:"flex-col justify-start",bottom:"flex-col justify-end"},v=[c,"relative flex flex-col overflow-hidden","text-(--neba-fg) bg-(--n-panel-press)","[border-color:var(--n-line)]","[outline:none]"].join(" "),y=["[transition:opacity_var(--neba-duration)_var(--neba-ease)]","data-[starting-style]:opacity-0 data-[ending-style]:opacity-0"].join(" "),w=["fixed inset-0 z-50 bg-(--neba-scrim)","[backdrop-filter:blur(2px)]",y].join(" "),j="border-t [border-color:var(--n-line)]",N=["flex size-[1.6em] shrink-0 cursor-pointer items-center justify-center","rounded-full text-(--neba-muted-fg)","[&_svg]:size-[1.1em] [&_svg]:shrink-0","[transition:background-color_var(--neba-duration)_var(--neba-ease),color_var(--neba-duration)_var(--neba-ease)]","hover:bg-(--n-soft) hover:text-(--neba-fg)","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-2"].join(" ");export const DrawerClose=l.Close;export function Drawer({side:c="left",mode:$="overlay",size:_="md",color:k="primary",density:C="default",open:z,defaultOpen:B,onOpenChange:D,trigger:O,title:P,description:T,actions:F,dividers:L=!1,showClose:R,closeLabel:V="Close",extent:q,rounded:A=!0,modal:E=!0,dismissible:G=!0,className:H,style:I,children:J}){const K="overlay"===$,M="left"===c||"right"===c,Q=R??K,S=n[C][_],U=o[C][_],W=L?`${S} ${U}`:S,X=i(P)||i(T),Y=i(F),Z=void 0===q?null:{[M?"width":"height"]:"number"==typeof q?`${q}px`:q},ee=[v,t[_],g[c],A?p[c][_]:"",K?"[box-shadow:var(--neba-shadow-3),var(--neba-plate-glass)]":"[box-shadow:var(--neba-plate-glass)]",K?y:"",M?`h-full max-w-full ${void 0===q?x[_]:""}`:"w-full "+(void 0===q?"max-h-[85%]":""),L?"":`${U} ${b[_]}`,H??""].filter(Boolean).join(" "),re=K?l.Title:"h2",ae=K?l.Description:"p",le=r(a,{children:[X||Q?r("div",{className:`flex shrink-0 items-start gap-3 ${W}`,children:[r("div",{className:`flex min-w-0 flex-1 flex-col ${u[_]}`,children:[i(P)?e(re,{className:`m-0 font-semibold ${m[_]}`,children:P}):null,i(T)?e(ae,{className:`m-0 text-(--neba-muted-fg) ${d[_]}`,children:T}):null]}),Q?K?e(l.Close,{"aria-label":V,className:N,children:e(s,{})}):e("button",{type:"button","aria-label":V,className:N,onClick:()=>D?.(!1),children:e(s,{})}):null]}):null,i(J)?e("div",{className:["min-h-0 flex-1 overflow-y-auto overscroll-contain",W,L&&(X||Q)?j:"",L?"":"py-1 -my-1"].filter(Boolean).join(" "),children:J}):null,Y?e("div",{className:["flex shrink-0 flex-wrap items-center justify-end gap-2",W,L?j:""].filter(Boolean).join(" "),children:F}):null]});return K?r(l.Root,{open:z,defaultOpen:B,modal:E,disablePointerDismissal:!G,onOpenChange:(e,r)=>{G||e||"escape-key"!==r.reason?D?.(e):r.cancel()},children:[O?e(l.Trigger,{render:O}):null,r(l.Portal,{children:[e(l.Backdrop,{className:`neba-portal ${w}`}),e(l.Viewport,{className:`neba-portal fixed inset-0 z-50 flex ${h[c]}`,children:e(l.Popup,{className:ee,style:{...f(k,3),...Z,...I},children:le})})]})]}):z??B??1?e("div",{className:ee,style:{...f(k,0),...Z,...I},children:le}):null}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Drawer,DrawerClose}from"./Drawer";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type { NebaColor, NebaSize } from '../../types';
|
|
2
|
+
import type { NebaColor, NebaSize, NebaTransition } from '../../types';
|
|
3
3
|
export interface IconProps extends Omit<React.ComponentPropsWithoutRef<'span'>, 'color'> {
|
|
4
4
|
/**
|
|
5
5
|
* The glyph. An `<svg>` element, an `<img>`, a component from an icon set, or
|
|
@@ -13,6 +13,13 @@ export interface IconProps extends Omit<React.ComponentPropsWithoutRef<'span'>,
|
|
|
13
13
|
* *sizes*, not content the Icon merely wraps.
|
|
14
14
|
*/
|
|
15
15
|
icon: React.ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* An entrance animation, run once on mount: `transition="fade"`, or an object
|
|
18
|
+
* for the details. `transition="rotate"` with `repeat="infinite"` is the one
|
|
19
|
+
* spin the design language allows on a glyph. For a trigger or a replay, wrap
|
|
20
|
+
* it in an `Animate*` component instead.
|
|
21
|
+
*/
|
|
22
|
+
transition?: NebaTransition;
|
|
16
23
|
/**
|
|
17
24
|
* The box the glyph is drawn in: 14, 16, 20, 24 and 28px. Its own ladder
|
|
18
25
|
* rather than the control heights, because an icon is not a control.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e}from"react/jsx-runtime";import*as t from"react";import{
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import*as t from"react";import{transitionProps as i}from"../../internal/animate";import{iconSizeClasses as n}from"../../internal/styles";const r={xs:"text-[0.875rem]",sm:"text-[1rem]",md:"text-[1.25rem]",lg:"text-[1.5rem]",xl:"text-[1.75rem]"};export const Icon=t.forwardRef(function({icon:t,size:l="md",color:a="inherit",label:s,transition:o,className:m,style:c,...f},x){const d=i(o),g=["inline-flex shrink-0 items-center justify-center align-middle",n[l],"[&>svg]:block [&>svg]:size-full [&>img]:block [&>img]:size-full",`leading-none ${r[l]}`,"inherit"===a?"":"text-(--n-accent)",d.className,m??""].filter(Boolean).join(" ");return e("span",{ref:x,className:g,style:"inherit"===a?{...d.style,...c}:{"--n-accent":`var(--neba-${a}-accent)`,...d.style,...c},role:s?"img":void 0,"aria-label":s,"aria-hidden":!s||void 0,...f,children:t})});
|
|
@@ -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";
|