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.
Files changed (123) hide show
  1. package/README.md +12 -7
  2. package/dist/components/alert/Alert.d.ts +7 -1
  3. package/dist/components/alert/Alert.js +1 -1
  4. package/dist/components/animate-appear/AnimateAppear.d.ts +55 -0
  5. package/dist/components/animate-appear/AnimateAppear.js +1 -0
  6. package/dist/components/animate-appear/index.d.ts +2 -0
  7. package/dist/components/animate-appear/index.js +1 -0
  8. package/dist/components/animate-blink/AnimateBlink.d.ts +31 -0
  9. package/dist/components/animate-blink/AnimateBlink.js +1 -0
  10. package/dist/components/animate-blink/index.d.ts +2 -0
  11. package/dist/components/animate-blink/index.js +1 -0
  12. package/dist/components/animate-fade/AnimateFade.d.ts +31 -0
  13. package/dist/components/animate-fade/AnimateFade.js +1 -0
  14. package/dist/components/animate-fade/index.d.ts +2 -0
  15. package/dist/components/animate-fade/index.js +1 -0
  16. package/dist/components/animate-grow/AnimateGrow.d.ts +41 -0
  17. package/dist/components/animate-grow/AnimateGrow.js +1 -0
  18. package/dist/components/animate-grow/index.d.ts +2 -0
  19. package/dist/components/animate-grow/index.js +1 -0
  20. package/dist/components/animate-headline/AnimateHeadline.d.ts +55 -0
  21. package/dist/components/animate-headline/AnimateHeadline.js +1 -0
  22. package/dist/components/animate-headline/index.d.ts +2 -0
  23. package/dist/components/animate-headline/index.js +1 -0
  24. package/dist/components/animate-lighting/AnimateLighting.d.ts +62 -0
  25. package/dist/components/animate-lighting/AnimateLighting.js +1 -0
  26. package/dist/components/animate-lighting/index.d.ts +2 -0
  27. package/dist/components/animate-lighting/index.js +1 -0
  28. package/dist/components/animate-marquee/AnimateMarquee.d.ts +65 -0
  29. package/dist/components/animate-marquee/AnimateMarquee.js +1 -0
  30. package/dist/components/animate-marquee/index.d.ts +2 -0
  31. package/dist/components/animate-marquee/index.js +1 -0
  32. package/dist/components/animate-rotate/AnimateRotate.d.ts +48 -0
  33. package/dist/components/animate-rotate/AnimateRotate.js +1 -0
  34. package/dist/components/animate-rotate/index.d.ts +2 -0
  35. package/dist/components/animate-rotate/index.js +1 -0
  36. package/dist/components/animate-slide/AnimateSlide.d.ts +46 -0
  37. package/dist/components/animate-slide/AnimateSlide.js +1 -0
  38. package/dist/components/animate-slide/index.d.ts +2 -0
  39. package/dist/components/animate-slide/index.js +1 -0
  40. package/dist/components/animate-typing/AnimateTyping.d.ts +58 -0
  41. package/dist/components/animate-typing/AnimateTyping.js +1 -0
  42. package/dist/components/animate-typing/index.d.ts +2 -0
  43. package/dist/components/animate-typing/index.js +1 -0
  44. package/dist/components/animate-zoom/AnimateZoom.d.ts +37 -0
  45. package/dist/components/animate-zoom/AnimateZoom.js +1 -0
  46. package/dist/components/animate-zoom/index.d.ts +2 -0
  47. package/dist/components/animate-zoom/index.js +1 -0
  48. package/dist/components/aspect-ratio/AspectRatio.d.ts +63 -0
  49. package/dist/components/aspect-ratio/AspectRatio.js +1 -0
  50. package/dist/components/aspect-ratio/index.d.ts +2 -0
  51. package/dist/components/aspect-ratio/index.js +1 -0
  52. package/dist/components/avatar/Avatar.d.ts +106 -0
  53. package/dist/components/avatar/Avatar.js +1 -0
  54. package/dist/components/avatar/index.d.ts +2 -0
  55. package/dist/components/avatar/index.js +1 -0
  56. package/dist/components/blockquote/Blockquote.d.ts +7 -1
  57. package/dist/components/blockquote/Blockquote.js +1 -1
  58. package/dist/components/box/Box.d.ts +7 -1
  59. package/dist/components/box/Box.js +1 -1
  60. package/dist/components/breadcrumb/Breadcrumb.d.ts +92 -0
  61. package/dist/components/breadcrumb/Breadcrumb.js +1 -0
  62. package/dist/components/breadcrumb/index.d.ts +2 -0
  63. package/dist/components/breadcrumb/index.js +1 -0
  64. package/dist/components/chat-bubble/ChatBubble.d.ts +114 -0
  65. package/dist/components/chat-bubble/ChatBubble.js +1 -0
  66. package/dist/components/chat-bubble/index.d.ts +2 -0
  67. package/dist/components/chat-bubble/index.js +1 -0
  68. package/dist/components/chip/Chip.d.ts +7 -1
  69. package/dist/components/chip/Chip.js +1 -1
  70. package/dist/components/color-picker/ColorPicker.d.ts +102 -0
  71. package/dist/components/color-picker/ColorPicker.js +1 -0
  72. package/dist/components/color-picker/index.d.ts +2 -0
  73. package/dist/components/color-picker/index.js +1 -0
  74. package/dist/components/drawer/Drawer.d.ts +145 -0
  75. package/dist/components/drawer/Drawer.js +1 -0
  76. package/dist/components/drawer/index.d.ts +2 -0
  77. package/dist/components/drawer/index.js +1 -0
  78. package/dist/components/icon/Icon.d.ts +8 -1
  79. package/dist/components/icon/Icon.js +1 -1
  80. package/dist/components/otp-field/OtpField.d.ts +85 -0
  81. package/dist/components/otp-field/OtpField.js +1 -0
  82. package/dist/components/otp-field/index.d.ts +2 -0
  83. package/dist/components/otp-field/index.js +1 -0
  84. package/dist/components/panes/Panes.d.ts +78 -0
  85. package/dist/components/panes/Panes.js +1 -0
  86. package/dist/components/panes/index.d.ts +2 -0
  87. package/dist/components/panes/index.js +1 -0
  88. package/dist/components/popover/Popover.d.ts +109 -0
  89. package/dist/components/popover/Popover.js +1 -0
  90. package/dist/components/popover/index.d.ts +2 -0
  91. package/dist/components/popover/index.js +1 -0
  92. package/dist/components/skeleton/Skeleton.d.ts +76 -0
  93. package/dist/components/skeleton/Skeleton.js +1 -0
  94. package/dist/components/skeleton/index.d.ts +2 -0
  95. package/dist/components/skeleton/index.js +1 -0
  96. package/dist/components/spoiler/Spoiler.d.ts +95 -0
  97. package/dist/components/spoiler/Spoiler.js +1 -0
  98. package/dist/components/spoiler/index.d.ts +2 -0
  99. package/dist/components/spoiler/index.js +1 -0
  100. package/dist/components/text-link/TextLink.d.ts +82 -0
  101. package/dist/components/text-link/TextLink.js +1 -0
  102. package/dist/components/text-link/index.d.ts +2 -0
  103. package/dist/components/text-link/index.js +1 -0
  104. package/dist/components/tree-view/TreeView.d.ts +115 -0
  105. package/dist/components/tree-view/TreeView.js +1 -0
  106. package/dist/components/tree-view/index.d.ts +2 -0
  107. package/dist/components/tree-view/index.js +1 -0
  108. package/dist/components/typography/Typography.d.ts +7 -1
  109. package/dist/components/typography/Typography.js +1 -1
  110. package/dist/index.d.ts +24 -0
  111. package/dist/index.js +1 -1
  112. package/dist/internal/animate.d.ts +175 -0
  113. package/dist/internal/animate.js +1 -0
  114. package/dist/internal/color.d.ts +108 -0
  115. package/dist/internal/color.js +1 -0
  116. package/dist/internal/i18n.d.ts +103 -0
  117. package/dist/internal/i18n.js +1 -0
  118. package/dist/internal/icons.d.ts +23 -0
  119. package/dist/internal/icons.js +1 -1
  120. package/dist/styles.css +1 -1
  121. package/dist/tailwind.css +504 -0
  122. package/dist/types.d.ts +112 -0
  123. 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,2 @@
1
+ export { ChatBubble } from './ChatBubble';
2
+ export type { ChatBubbleLinkPreview, ChatBubbleProps, ChatBubbleSide, ChatBubbleStatus } from './ChatBubble';
@@ -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 l}from"react/jsx-runtime";import*as t from"react";import{CloseIcon as o}from"../../internal/icons";import{chipRemoveClasses as i,controlHeightClasses as r,controlSlots as s,controlTextClasses as a,disabledClasses as c,focusRingClasses as d,gapClasses as m,iconClasses as p,paddingXClasses as b,pressTransitionClasses as f,radiusClasses as u,surfaceClasses as h,transitionClasses as v}from"../../internal/styles";const x={xs:"xs",sm:"xs",md:"sm",lg:"md",xl:"lg"},g={solid:[h,"text-(--n-on-solid) bg-(--n-fill)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[h,"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)"},j={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)"},y=["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]",v,f,p].join(" "),k=["flex min-w-0 flex-1 cursor-pointer items-center justify-center self-stretch rounded-[inherit]",d].join(" ");export const Chip=t.forwardRef(function({variant:t="outline",size:d="md",color:p="primary",density:f="default",elevation:h=0,startIcon:v,endIcon:N,count:C,onDelete:$,deleteLabel:B="Remove",selected:I=!1,disabled:R=!1,className:z,style:D,children:F,onClick:L,...q},A){const E=Boolean(L)&&!R,G=x[d],H=b[f][G],J=[y,r[G],a[G],m[G],u[G],R?c[t]:g[t],!R&&I?j[t]:"",E?w[t]:"",E?"ps-0":H,$?"pe-1":E?"pe-0":"",z??""].filter(Boolean).join(" "),K=l(n,{children:[v,null!=F&&!1!==F?e("span",{className:"min-w-0 truncate",children:F}):null,N,null!=C&&!1!==C?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"===t?"bg-(--neba-glow-on-fill) text-(--n-on-solid)":"bg-(--n-soft-press) text-(--n-accent)"].join(" "),children:C}):null]});return l("span",{ref:A,className:J,style:{...s(p,h,t),...D},"aria-disabled":!(!R||E)||void 0,...q,children:[E?e("button",{type:"button","aria-pressed":I,className:`${k} ${m[G]} ${H}`,onClick:L,children:K}):K,$?e("button",{type:"button","aria-label":B,disabled:R,className:i,onClick:$,children:e(o,{})}):null]})});
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,2 @@
1
+ export { ColorPicker } from './ColorPicker';
2
+ export type { ColorPickerLabels, ColorPickerProps } from './ColorPicker';
@@ -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,2 @@
1
+ export { Drawer, DrawerClose } from './Drawer';
2
+ export type { DrawerCloseProps, DrawerProps, NebaDrawerMode } from './Drawer';
@@ -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{iconSizeClasses as i}from"../../internal/styles";const n={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:r="md",color:l="inherit",label:s,className:a,style:o,...c},m){const f=["inline-flex shrink-0 items-center justify-center align-middle",i[r],"[&>svg]:block [&>svg]:size-full [&>img]:block [&>img]:size-full",`leading-none ${n[r]}`,"inherit"===l?"":"text-(--n-accent)",a??""].filter(Boolean).join(" ");return e("span",{ref:m,className:f,style:"inherit"===l?o:{"--n-accent":`var(--neba-${l}-accent)`,...o},role:s?"img":void 0,"aria-label":s,"aria-hidden":!s||void 0,...c,children:t})});
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,2 @@
1
+ export { OtpField } from './OtpField';
2
+ export type { OtpFieldCharset, OtpFieldProps } from './OtpField';
@@ -0,0 +1 @@
1
+ export{OtpField}from"./OtpField";