neba 0.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/README.md +177 -2
  2. package/dist/components/accordion/Accordion.d.ts +97 -0
  3. package/dist/components/accordion/Accordion.js +1 -0
  4. package/dist/components/accordion/index.d.ts +2 -0
  5. package/dist/components/accordion/index.js +1 -0
  6. package/dist/components/alert/Alert.d.ts +47 -0
  7. package/dist/components/alert/Alert.js +1 -0
  8. package/dist/components/alert/index.d.ts +2 -0
  9. package/dist/components/alert/index.js +1 -0
  10. package/dist/components/badge/Badge.d.ts +80 -0
  11. package/dist/components/badge/Badge.js +1 -0
  12. package/dist/components/badge/index.d.ts +2 -0
  13. package/dist/components/badge/index.js +1 -0
  14. package/dist/components/blockquote/Blockquote.d.ts +65 -0
  15. package/dist/components/blockquote/Blockquote.js +1 -0
  16. package/dist/components/blockquote/index.d.ts +2 -0
  17. package/dist/components/blockquote/index.js +1 -0
  18. package/dist/components/box/Box.d.ts +50 -0
  19. package/dist/components/box/Box.js +1 -0
  20. package/dist/components/box/index.d.ts +2 -0
  21. package/dist/components/box/index.js +1 -0
  22. package/dist/components/button/Button.d.ts +25 -3
  23. package/dist/components/button/Button.js +1 -1
  24. package/dist/components/button/index.d.ts +1 -0
  25. package/dist/components/button-group/ButtonGroup.d.ts +31 -0
  26. package/dist/components/button-group/ButtonGroup.js +1 -0
  27. package/dist/components/button-group/index.d.ts +2 -0
  28. package/dist/components/button-group/index.js +1 -0
  29. package/dist/components/card/Card.d.ts +45 -0
  30. package/dist/components/card/Card.js +1 -0
  31. package/dist/components/card/index.d.ts +2 -0
  32. package/dist/components/card/index.js +1 -0
  33. package/dist/components/carousel/Carousel.d.ts +74 -0
  34. package/dist/components/carousel/Carousel.js +1 -0
  35. package/dist/components/carousel/index.d.ts +2 -0
  36. package/dist/components/carousel/index.js +1 -0
  37. package/dist/components/checkbox/Checkbox.d.ts +39 -0
  38. package/dist/components/checkbox/Checkbox.js +1 -0
  39. package/dist/components/checkbox/index.d.ts +2 -0
  40. package/dist/components/checkbox/index.js +1 -0
  41. package/dist/components/chip/Chip.d.ts +50 -0
  42. package/dist/components/chip/Chip.js +1 -0
  43. package/dist/components/chip/index.d.ts +2 -0
  44. package/dist/components/chip/index.js +1 -0
  45. package/dist/components/combobox/Combobox.d.ts +131 -0
  46. package/dist/components/combobox/Combobox.js +1 -0
  47. package/dist/components/combobox/index.d.ts +2 -0
  48. package/dist/components/combobox/index.js +1 -0
  49. package/dist/components/container/Container.d.ts +55 -0
  50. package/dist/components/container/Container.js +1 -0
  51. package/dist/components/container/index.d.ts +2 -0
  52. package/dist/components/container/index.js +1 -0
  53. package/dist/components/date-picker/DatePicker.d.ts +75 -0
  54. package/dist/components/date-picker/DatePicker.js +1 -0
  55. package/dist/components/date-picker/index.d.ts +2 -0
  56. package/dist/components/date-picker/index.js +1 -0
  57. package/dist/components/date-range-picker/DateRangePicker.d.ts +81 -0
  58. package/dist/components/date-range-picker/DateRangePicker.js +1 -0
  59. package/dist/components/date-range-picker/index.d.ts +2 -0
  60. package/dist/components/date-range-picker/index.js +1 -0
  61. package/dist/components/date-time-picker/DateTimePicker.d.ts +68 -0
  62. package/dist/components/date-time-picker/DateTimePicker.js +1 -0
  63. package/dist/components/date-time-picker/index.d.ts +2 -0
  64. package/dist/components/date-time-picker/index.js +1 -0
  65. package/dist/components/dialog/Dialog.d.ts +111 -0
  66. package/dist/components/dialog/Dialog.js +1 -0
  67. package/dist/components/dialog/index.d.ts +2 -0
  68. package/dist/components/dialog/index.js +1 -0
  69. package/dist/components/divider/Divider.d.ts +67 -0
  70. package/dist/components/divider/Divider.js +1 -0
  71. package/dist/components/divider/index.d.ts +2 -0
  72. package/dist/components/divider/index.js +1 -0
  73. package/dist/components/file-picker/FilePicker.d.ts +103 -0
  74. package/dist/components/file-picker/FilePicker.js +1 -0
  75. package/dist/components/file-picker/index.d.ts +2 -0
  76. package/dist/components/file-picker/index.js +1 -0
  77. package/dist/components/grid/Grid.d.ts +50 -0
  78. package/dist/components/grid/Grid.js +1 -0
  79. package/dist/components/grid/index.d.ts +2 -0
  80. package/dist/components/grid/index.js +1 -0
  81. package/dist/components/grid-container/GridContainer.d.ts +79 -0
  82. package/dist/components/grid-container/GridContainer.js +1 -0
  83. package/dist/components/grid-container/index.d.ts +2 -0
  84. package/dist/components/grid-container/index.js +1 -0
  85. package/dist/components/highlight/Highlight.d.ts +84 -0
  86. package/dist/components/highlight/Highlight.js +1 -0
  87. package/dist/components/highlight/index.d.ts +2 -0
  88. package/dist/components/highlight/index.js +1 -0
  89. package/dist/components/icon/Icon.d.ts +58 -0
  90. package/dist/components/icon/Icon.js +1 -0
  91. package/dist/components/icon/index.d.ts +2 -0
  92. package/dist/components/icon/index.js +1 -0
  93. package/dist/components/icon-button/IconButton.d.ts +39 -0
  94. package/dist/components/icon-button/IconButton.js +1 -0
  95. package/dist/components/icon-button/index.d.ts +2 -0
  96. package/dist/components/icon-button/index.js +1 -0
  97. package/dist/components/list/List.d.ts +81 -0
  98. package/dist/components/list/List.js +1 -0
  99. package/dist/components/list/index.d.ts +2 -0
  100. package/dist/components/list/index.js +1 -0
  101. package/dist/components/menu/Menu.d.ts +216 -0
  102. package/dist/components/menu/Menu.js +1 -0
  103. package/dist/components/menu/index.d.ts +2 -0
  104. package/dist/components/menu/index.js +1 -0
  105. package/dist/components/number-field/NumberField.d.ts +113 -0
  106. package/dist/components/number-field/NumberField.js +1 -0
  107. package/dist/components/number-field/index.d.ts +2 -0
  108. package/dist/components/number-field/index.js +1 -0
  109. package/dist/components/overlay/Overlay.d.ts +85 -0
  110. package/dist/components/overlay/Overlay.js +1 -0
  111. package/dist/components/overlay/index.d.ts +2 -0
  112. package/dist/components/overlay/index.js +1 -0
  113. package/dist/components/pagination/Pagination.d.ts +65 -0
  114. package/dist/components/pagination/Pagination.js +1 -0
  115. package/dist/components/pagination/index.d.ts +2 -0
  116. package/dist/components/pagination/index.js +1 -0
  117. package/dist/components/pill/Pill.d.ts +80 -0
  118. package/dist/components/pill/Pill.js +1 -0
  119. package/dist/components/pill/index.d.ts +2 -0
  120. package/dist/components/pill/index.js +1 -0
  121. package/dist/components/progress-box/ProgressBox.d.ts +33 -0
  122. package/dist/components/progress-box/ProgressBox.js +1 -0
  123. package/dist/components/progress-box/index.d.ts +2 -0
  124. package/dist/components/progress-box/index.js +1 -0
  125. package/dist/components/progress-circular/ProgressCircular.d.ts +19 -0
  126. package/dist/components/progress-circular/ProgressCircular.js +1 -0
  127. package/dist/components/progress-circular/index.d.ts +2 -0
  128. package/dist/components/progress-circular/index.js +1 -0
  129. package/dist/components/progress-linear/ProgressLinear.d.ts +20 -0
  130. package/dist/components/progress-linear/ProgressLinear.js +1 -0
  131. package/dist/components/progress-linear/index.d.ts +2 -0
  132. package/dist/components/progress-linear/index.js +1 -0
  133. package/dist/components/radio-group/RadioGroup.d.ts +51 -0
  134. package/dist/components/radio-group/RadioGroup.js +1 -0
  135. package/dist/components/radio-group/index.d.ts +2 -0
  136. package/dist/components/radio-group/index.js +1 -0
  137. package/dist/components/segmented-button/SegmentedButton.d.ts +77 -0
  138. package/dist/components/segmented-button/SegmentedButton.js +1 -0
  139. package/dist/components/segmented-button/index.d.ts +2 -0
  140. package/dist/components/segmented-button/index.js +1 -0
  141. package/dist/components/select/Select.d.ts +76 -0
  142. package/dist/components/select/Select.js +1 -0
  143. package/dist/components/select/index.d.ts +2 -0
  144. package/dist/components/select/index.js +1 -0
  145. package/dist/components/shortcut/Shortcut.d.ts +60 -0
  146. package/dist/components/shortcut/Shortcut.js +1 -0
  147. package/dist/components/shortcut/index.d.ts +2 -0
  148. package/dist/components/shortcut/index.js +1 -0
  149. package/dist/components/slider/Slider.d.ts +38 -0
  150. package/dist/components/slider/Slider.js +1 -0
  151. package/dist/components/slider/index.d.ts +2 -0
  152. package/dist/components/slider/index.js +1 -0
  153. package/dist/components/statistic/Statistic.d.ts +91 -0
  154. package/dist/components/statistic/Statistic.js +1 -0
  155. package/dist/components/statistic/index.d.ts +2 -0
  156. package/dist/components/statistic/index.js +1 -0
  157. package/dist/components/switch/Switch.d.ts +39 -0
  158. package/dist/components/switch/Switch.js +1 -0
  159. package/dist/components/switch/index.d.ts +2 -0
  160. package/dist/components/switch/index.js +1 -0
  161. package/dist/components/table/Table.d.ts +80 -0
  162. package/dist/components/table/Table.js +1 -0
  163. package/dist/components/table/index.d.ts +2 -0
  164. package/dist/components/table/index.js +1 -0
  165. package/dist/components/tabs/Tabs.d.ts +92 -0
  166. package/dist/components/tabs/Tabs.js +1 -0
  167. package/dist/components/tabs/index.d.ts +2 -0
  168. package/dist/components/tabs/index.js +1 -0
  169. package/dist/components/text-field/TextField.d.ts +63 -0
  170. package/dist/components/text-field/TextField.js +1 -0
  171. package/dist/components/text-field/index.d.ts +2 -0
  172. package/dist/components/text-field/index.js +1 -0
  173. package/dist/components/time-picker/TimePicker.d.ts +76 -0
  174. package/dist/components/time-picker/TimePicker.js +1 -0
  175. package/dist/components/time-picker/index.d.ts +2 -0
  176. package/dist/components/time-picker/index.js +1 -0
  177. package/dist/components/timeline/Timeline.d.ts +98 -0
  178. package/dist/components/timeline/Timeline.js +1 -0
  179. package/dist/components/timeline/index.d.ts +2 -0
  180. package/dist/components/timeline/index.js +1 -0
  181. package/dist/components/toast/Toast.d.ts +116 -0
  182. package/dist/components/toast/Toast.js +1 -0
  183. package/dist/components/toast/index.d.ts +2 -0
  184. package/dist/components/toast/index.js +1 -0
  185. package/dist/components/toolbar/Toolbar.d.ts +67 -0
  186. package/dist/components/toolbar/Toolbar.js +1 -0
  187. package/dist/components/toolbar/index.d.ts +2 -0
  188. package/dist/components/toolbar/index.js +1 -0
  189. package/dist/components/tooltip/Tooltip.d.ts +81 -0
  190. package/dist/components/tooltip/Tooltip.js +1 -0
  191. package/dist/components/tooltip/index.d.ts +2 -0
  192. package/dist/components/tooltip/index.js +1 -0
  193. package/dist/components/typography/Typography.d.ts +62 -0
  194. package/dist/components/typography/Typography.js +1 -0
  195. package/dist/components/typography/index.d.ts +2 -0
  196. package/dist/components/typography/index.js +1 -0
  197. package/dist/index.d.ts +50 -1
  198. package/dist/index.js +1 -1
  199. package/dist/internal/button-group.d.ts +25 -0
  200. package/dist/internal/button-group.js +1 -0
  201. package/dist/internal/calendar.d.ts +149 -0
  202. package/dist/internal/calendar.js +1 -0
  203. package/dist/internal/date.d.ts +170 -0
  204. package/dist/internal/date.js +1 -0
  205. package/dist/internal/grid.d.ts +69 -0
  206. package/dist/internal/grid.js +1 -0
  207. package/dist/internal/icons.d.ts +79 -0
  208. package/dist/internal/icons.js +1 -0
  209. package/dist/internal/menu.d.ts +20 -0
  210. package/dist/internal/menu.js +1 -0
  211. package/dist/internal/picker.d.ts +111 -0
  212. package/dist/internal/picker.js +1 -0
  213. package/dist/internal/progress.d.ts +122 -0
  214. package/dist/internal/progress.js +1 -0
  215. package/dist/internal/sizer.d.ts +33 -0
  216. package/dist/internal/sizer.js +1 -0
  217. package/dist/internal/styles.d.ts +257 -0
  218. package/dist/internal/styles.js +1 -0
  219. package/dist/styles.css +2 -0
  220. package/dist/tailwind.css +892 -0
  221. package/dist/types.d.ts +142 -0
  222. package/dist/types.js +8 -0
  223. package/package.json +90 -21
  224. package/pnpm-workspace.yaml +0 -2
@@ -0,0 +1 @@
1
+ export{Pill}from"./Pill";
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+ import { type ProgressSharedProps } from '../../internal/progress';
3
+ import type { NebaColor, NebaSize } from '../../types';
4
+ export interface ProgressBoxProps extends ProgressSharedProps {
5
+ /** Size of one plate. */
6
+ size?: NebaSize;
7
+ /** @default 'primary' */
8
+ color?: NebaColor;
9
+ /**
10
+ * How many plates the row is made of.
11
+ *
12
+ * Four by default: enough that the wave reads as a wave, few enough that a
13
+ * determinate row can be counted at a glance rather than measured. Set it to
14
+ * the number of steps when the thing being waited on genuinely has steps.
15
+ * @default 4
16
+ */
17
+ count?: number;
18
+ }
19
+ /**
20
+ * A row of acrylic plates that light up.
21
+ *
22
+ * The third shape, and the one that is about the material rather than about the
23
+ * quantity. A bar and a ring both say "this much of it is done"; a row of
24
+ * plates says "this is working" in the library's own vocabulary — the same cut
25
+ * sheet, the same hairline, the same fill — which is what makes it the right
26
+ * one for a loading state inside a Neba surface where a foreign grey spinner
27
+ * would look borrowed.
28
+ *
29
+ * It answers a value when it has one: the plates fill left to right, the last
30
+ * one partially, so four plates read as a four-segment bar. Without a value
31
+ * they cycle, each held back by its own index.
32
+ */
33
+ export declare const ProgressBox: React.ForwardRefExoticComponent<ProgressBoxProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as l from"react";import{Progress as t}from"@base-ui/react/progress";import{plateGapClasses as n,plateRadiusClasses as r,plateSizeClasses as s,progressAriaText as i,progressFraction as o,progressSlots as m,progressText as f}from"../../internal/progress";import{metaTextClasses as c,stackGapClasses as u}from"../../internal/styles";export const ProgressBox=l.forwardRef(function({size:l="md",color:d="primary",count:h=4,value:x=null,min:b=0,max:p=100,label:g,showValue:v=!1,format:y,className:w,style:j},N){const M=o(x,b,p),B=null===M,V=void 0!==y,k=Math.max(1,Math.floor(h));return a(t.Root,{ref:N,value:x??null,min:b,max:p,format:y,getAriaValueText:i(M,V),className:["inline-flex flex-col",u[l],w??""].filter(Boolean).join(" "),style:{...m(d),...j},children:[g||v?a("div",{className:["flex items-baseline gap-2",g?"justify-between":"justify-end",c[l]].join(" "),children:[g?e(t.Label,{className:"min-w-0 truncate text-(--neba-fg)",children:g}):null,v?e(t.Value,{className:"shrink-0 tabular-nums text-(--neba-muted-fg)",children:e=>f(M,e,V)}):null]}):null,e(t.Track,{className:`flex ${n[l]}`,children:Array.from({length:k},(a,t)=>e("span",{className:["relative overflow-hidden bg-(--n-soft)","[box-shadow:var(--neba-plate-glass)]",s[l],r[l],B?"neba-plate-wave":""].filter(Boolean).join(" "),style:B?{"--n-i":t}:void 0,children:B?null:e("span",{className:"absolute inset-y-0 start-0 bg-(--n-fill) [transition:width_var(--neba-duration-fill)_var(--neba-ease)]",style:{width:`${Math.min(100,Math.max(0,100*(M*k-t)))}%`}})},t))})]})});
@@ -0,0 +1,2 @@
1
+ export { ProgressBox } from './ProgressBox';
2
+ export type { ProgressBoxProps } from './ProgressBox';
@@ -0,0 +1 @@
1
+ export{ProgressBox}from"./ProgressBox";
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+ import { type ProgressSharedProps } from '../../internal/progress';
3
+ import type { NebaColor, NebaSize } from '../../types';
4
+ export interface ProgressCircularProps extends ProgressSharedProps {
5
+ /** Diameter of the ring. Sits just under the control ladder at every step. */
6
+ size?: NebaSize;
7
+ /** @default 'primary' */
8
+ color?: NebaColor;
9
+ }
10
+ /**
11
+ * A ring that fills, and the one to reach for when there is no room for a bar —
12
+ * inside a button, at the end of a table row, next to a field.
13
+ *
14
+ * The value and the label sit *beside* the ring rather than inside it. A number
15
+ * in the middle of a dial is the picture everyone has of this component, and it
16
+ * only works at two of the five sizes: at `xs` the ring is fourteen pixels
17
+ * across and there is nowhere for "40%" to go. Beside it, every size reads.
18
+ */
19
+ export declare const ProgressCircular: React.ForwardRefExoticComponent<ProgressCircularProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as a from"react";import{Progress as t}from"@base-ui/react/progress";import{progressAriaText as s,progressFraction as n,progressSlots as i,progressText as o,ringDiameters as l,ringStrokes as c}from"../../internal/progress";import{gapClasses as m,metaTextClasses as f}from"../../internal/styles";export const ProgressCircular=a.forwardRef(function({size:a="md",color:u="primary",value:h=null,min:d=0,max:x=100,label:k,showValue:p=!1,format:b,className:g,style:v},y){const $=n(h,d,x),N=null===$,w=void 0!==b,j=l[a],V=c[a],B=j/2,D=B-V/2,L=2*Math.PI*D,P=N?`${.28*L} ${L}`:`${L}`,R=N?0:L*(1-$);return r(t.Root,{ref:y,value:h??null,min:d,max:x,format:b,getAriaValueText:s($,w),className:["inline-flex items-center",m[a],f[a],g??""].filter(Boolean).join(" "),style:{...i(u),...v},children:[r("svg",{className:N?"neba-ring-spin shrink-0":"shrink-0",width:j,height:j,viewBox:`0 0 ${j} ${j}`,fill:"none","aria-hidden":"true",children:[e("circle",{cx:B,cy:B,r:D,stroke:"var(--n-soft)",strokeWidth:V}),e("circle",{cx:B,cy:B,r:D,stroke:"var(--n-accent)",strokeWidth:V,strokeLinecap:"round",strokeDasharray:P,strokeDashoffset:R,transform:`rotate(-90 ${B} ${B})`,className:"[transition:stroke-dashoffset_var(--neba-duration-fill)_var(--neba-ease)]"})]}),k?e(t.Label,{className:"min-w-0 truncate text-(--neba-fg)",children:k}):null,p?e(t.Value,{className:"shrink-0 tabular-nums text-(--neba-muted-fg)",children:e=>o($,e,w)}):null]})});
@@ -0,0 +1,2 @@
1
+ export { ProgressCircular } from './ProgressCircular';
2
+ export type { ProgressCircularProps } from './ProgressCircular';
@@ -0,0 +1 @@
1
+ export{ProgressCircular}from"./ProgressCircular";
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+ import { type ProgressSharedProps } from '../../internal/progress';
3
+ import type { NebaColor, NebaSize } from '../../types';
4
+ export interface ProgressLinearProps extends ProgressSharedProps {
5
+ /** Thickness of the groove. Nothing else on a bar has a size. */
6
+ size?: NebaSize;
7
+ /** @default 'primary' */
8
+ color?: NebaColor;
9
+ }
10
+ /**
11
+ * A bar that fills. The workhorse: it is the only one of the three that can
12
+ * show *how much* is left at a glance, because length is the one quantity a
13
+ * reader can compare without counting.
14
+ *
15
+ * Base UI's Progress owns the semantics — `role="progressbar"`, the value and
16
+ * range attributes, `aria-valuetext`, and dropping the value entirely when the
17
+ * bar is indeterminate — and it also computes the fill width, so the determinate
18
+ * case here is a class list and nothing else.
19
+ */
20
+ export declare const ProgressLinear: React.ForwardRefExoticComponent<ProgressLinearProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as l}from"react/jsx-runtime";import*as a from"react";import{Progress as r}from"@base-ui/react/progress";import{barThicknessClasses as n,progressAriaText as s,progressFraction as t,progressSlots as i,progressText as o}from"../../internal/progress";import{metaTextClasses as m,stackGapClasses as u}from"../../internal/styles";export const ProgressLinear=a.forwardRef(function({size:a="md",color:f="primary",value:c=null,min:d=0,max:b=100,label:p,showValue:x=!1,format:g,className:h,style:w},j){const v=t(c,d,b),N=null===v,y=void 0!==g;return l(r.Root,{ref:j,value:c??null,min:d,max:b,format:g,getAriaValueText:s(v,y),className:["flex w-full flex-col",u[a],h??""].filter(Boolean).join(" "),style:{...i(f),...w},children:[p||x?l("div",{className:["flex items-baseline gap-2",p?"justify-between":"justify-end",m[a]].join(" "),children:[p?e(r.Label,{className:"min-w-0 truncate text-(--neba-fg)",children:p}):null,x?e(r.Value,{className:"shrink-0 tabular-nums text-(--neba-muted-fg)",children:e=>o(v,e,y)}):null]}):null,e(r.Track,{className:`relative w-full overflow-hidden rounded-full bg-(--n-soft) ${n[a]}`,children:e(r.Indicator,{className:["absolute rounded-full bg-(--n-fill)",N?"neba-sweep":"top-0 [transition:width_var(--neba-duration-fill)_var(--neba-ease)]"].join(" ")})})]})});
@@ -0,0 +1,2 @@
1
+ export { ProgressLinear } from './ProgressLinear';
2
+ export type { ProgressLinearProps } from './ProgressLinear';
@@ -0,0 +1 @@
1
+ export{ProgressLinear}from"./ProgressLinear";
@@ -0,0 +1,51 @@
1
+ import * as React from 'react';
2
+ import { Radio as BaseUIRadio } from '@base-ui/react/radio';
3
+ import { RadioGroup as BaseUIRadioGroup } from '@base-ui/react/radio-group';
4
+ import type { NebaColor, NebaOrientation, NebaSize } from '../../types';
5
+ export interface RadioGroupProps extends Omit<React.ComponentPropsWithoutRef<typeof BaseUIRadioGroup>, 'className' | 'style' | 'render'> {
6
+ /** @default 'md' */
7
+ size?: NebaSize;
8
+ /** @default 'primary' */
9
+ color?: NebaColor;
10
+ /**
11
+ * Which way the options stack. Vertical by default — a column of options is
12
+ * scannable at any length, and a row silently becomes unreadable the moment
13
+ * one label is longer than expected.
14
+ * @default 'vertical'
15
+ */
16
+ orientation?: NebaOrientation;
17
+ /** The question the options answer. Rendered as the group's legend. */
18
+ label?: React.ReactNode;
19
+ /** Helper text under the label. */
20
+ description?: React.ReactNode;
21
+ /** Error message below the options. Its presence also turns the group invalid. */
22
+ error?: React.ReactNode;
23
+ /** Forces the invalid state without a message. Defaults to `!!error`. */
24
+ invalid?: boolean;
25
+ className?: string;
26
+ style?: React.CSSProperties;
27
+ }
28
+ export interface RadioProps extends Omit<React.ComponentPropsWithoutRef<typeof BaseUIRadio.Root>, 'className' | 'style' | 'render' | 'children'> {
29
+ /** The text beside the dot. Wired to it by Base UI's Field. */
30
+ label?: React.ReactNode;
31
+ /** Helper text under the label. */
32
+ description?: React.ReactNode;
33
+ className?: string;
34
+ style?: React.CSSProperties;
35
+ }
36
+ /**
37
+ * One option in a RadioGroup.
38
+ *
39
+ * It has no `size` and no `color` of its own — both come from the group, which
40
+ * is the only place they can be set once and mean the same thing for every
41
+ * option in the set.
42
+ */
43
+ export declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLElement>>;
44
+ /**
45
+ * A set of options where exactly one is chosen.
46
+ *
47
+ * Base UI owns the roving tab index and the arrow-key navigation, which is the
48
+ * whole reason a radio group is a component at all rather than a `<div>` full of
49
+ * inputs: the set takes one tab stop, and the arrows move within it.
50
+ */
51
+ export declare const RadioGroup: React.ForwardRefExoticComponent<RadioGroupProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as r from"react";import{Radio as n}from"@base-ui/react/radio";import{RadioGroup as l}from"@base-ui/react/radio-group";import{Field as o}from"@base-ui/react/field";import{controlTextClasses as i,metaTextClasses as t,surfaceClasses as s,tickDotClasses as d,tickRowLeadingClasses as c,tickSizeClasses as b,transitionClasses as f}from"../../internal/styles";const h=r.createContext({size:"md",color:"primary",readOnly:!1}),m=["relative inline-flex shrink-0 items-center justify-center rounded-full border","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",f,"active:[transition-duration:0ms]","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-2"].join(" "),u=[s,"cursor-pointer bg-(--n-panel) [border-color:var(--n-line)]","hover:bg-(--n-panel-hover) hover:[border-color:var(--n-line-hover)]","data-[checked]:bg-(--n-fill) data-[checked]:text-(--n-on-solid)","data-[checked]:[border-color:transparent]","data-[checked]:hover:bg-(--n-fill-hover)"].join(" "),p=[s,"cursor-default bg-(--n-panel) [border-color:var(--n-line)]","[filter:saturate(0.55)]","data-[checked]:bg-(--n-fill) data-[checked]:text-(--n-on-solid)","data-[checked]:[border-color:transparent]"].join(" "),v=["cursor-not-allowed bg-transparent [border-color:var(--neba-disabled-border)] shadow-none","data-[checked]:bg-(--neba-disabled-bg)"].join(" ");export const Radio=r.forwardRef(function({label:l,description:s,disabled:f=!1,className:x,style:g,...y},N){const j=r.useContext(h),$=y.readOnly??j.readOnly;return e(o.Root,{disabled:f,className:["flex flex-col",x??""].filter(Boolean).join(" "),style:g,children:a("div",{className:`flex items-start gap-2 ${i[j.size]} ${c}`,children:[e("span",{className:"flex h-[1lh] shrink-0 items-center",children:e(n.Root,{ref:N,className:[m,b[j.size],f?v:$?p:u].join(" "),disabled:f,...y,children:e(n.Indicator,{className:`rounded-full bg-current ${d[j.size]}`})})}),l||s?a("span",{className:"flex min-w-0 flex-col gap-0.5",children:[l?e(o.Label,{className:["leading-[1.4]",f?"text-(--neba-disabled-fg)":"cursor-pointer text-(--neba-fg)"].join(" "),children:l}):null,s?e(o.Description,{className:`${t[j.size]} text-(--neba-muted-fg)`,children:s}):null]}):null]})})});export const RadioGroup=r.forwardRef(function({size:n="md",color:i="primary",orientation:s="vertical",label:d,description:c,error:b,invalid:f,disabled:m=!1,readOnly:u=!1,className:p,style:v,children:x,...g},y){const N=null!=b&&!1!==b&&""!==b,j=f??N,$=j?"danger":i,k={"--n-fill":`var(--neba-${$}-fill)`,"--n-fill-hover":`var(--neba-${$}-fill-hover)`,"--n-on-solid":`var(--neba-${$}-on-solid)`,"--n-accent":`var(--neba-${$}-accent)`,"--n-panel":"var(--neba-panel)","--n-panel-hover":"var(--neba-panel-hover)","--n-line":`var(--neba-${$}-line)`,"--n-line-hover":`var(--neba-${$}-line-hover)`,"--n-ring":`var(--neba-${$}-ring)`},R=r.useMemo(()=>({size:n,color:$,readOnly:u}),[n,$,u]);return e(h.Provider,{value:R,children:a(o.Root,{disabled:m,invalid:j,className:["flex flex-col gap-1.5",p??""].filter(Boolean).join(" "),style:{...k,...v},children:[d?e(o.Label,{className:[t[n],"font-medium",m?"text-(--neba-disabled-fg)":"text-(--neba-fg)"].join(" "),children:d}):null,c?e(o.Description,{className:`${t[n]} text-(--neba-muted-fg)`,children:c}):null,e(l,{ref:y,disabled:m,readOnly:u,className:["flex","horizontal"===s?"flex-row flex-wrap gap-x-5 gap-y-2":"flex-col gap-2"].join(" "),...g,children:x}),N?e(o.Error,{match:!0,className:`${t[n]} text-(--n-accent)`,children:b}):null]})})});
@@ -0,0 +1,2 @@
1
+ export { Radio, RadioGroup } from './RadioGroup';
2
+ export type { RadioGroupProps, RadioProps } from './RadioGroup';
@@ -0,0 +1 @@
1
+ export{Radio,RadioGroup}from"./RadioGroup";
@@ -0,0 +1,77 @@
1
+ import * as React from 'react';
2
+ import type { NebaElevation, NebaStyleProps, NebaVariant } from '../../types';
3
+ /** A segment's value. The same restraint Tabs and Select put on theirs. */
4
+ export type SegmentValue = string | number;
5
+ export interface SegmentedButtonProps extends Omit<NebaStyleProps, 'variant'>, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'defaultValue' | 'onChange'> {
6
+ /**
7
+ * Weight of the trough the segments sit in.
8
+ *
9
+ * - `solid` — a frosted trough with a filled tile riding in it. The loudest,
10
+ * and the one for a control a screen is about to be steered by.
11
+ * - `outline` — the same trough with a hairline around it and a lit tile
12
+ * rather than a filled one. The default.
13
+ * - `text` — no trough at all: the segments sit straight on the page and only
14
+ * the chosen one has a surface.
15
+ * @default 'outline'
16
+ */
17
+ variant?: NebaVariant;
18
+ /** The chosen segment. Use with `onValueChange` for a controlled set. */
19
+ value?: SegmentValue | null;
20
+ /** Which starts chosen, for an uncontrolled set. */
21
+ defaultValue?: SegmentValue | null;
22
+ onValueChange?: (value: SegmentValue | null) => void;
23
+ /**
24
+ * Drop shadow depth of the trough. `0` (the default) is flat.
25
+ * @default 0
26
+ */
27
+ elevation?: NebaElevation;
28
+ /** Disables every segment at once. */
29
+ disabled?: boolean;
30
+ /** Shows which one is chosen but does not let it be changed. */
31
+ readOnly?: boolean;
32
+ /** Identifies the value when a form is submitted. */
33
+ name?: string;
34
+ /** The segments share the full width, each taking an equal part of it. */
35
+ fullWidth?: boolean;
36
+ children?: React.ReactNode;
37
+ }
38
+ export interface SegmentProps extends Omit<React.ComponentPropsWithoutRef<'span'>, 'value' | 'color'> {
39
+ /** Identifies the segment. What `onValueChange` reports. */
40
+ value: SegmentValue;
41
+ /** Content before the label. Sized in `em`, so it tracks the label. */
42
+ startIcon?: React.ReactNode;
43
+ /** Content after the label — a count, a status dot. */
44
+ endIcon?: React.ReactNode;
45
+ /** Unavailable, but still part of the set. */
46
+ disabled?: boolean;
47
+ children?: React.ReactNode;
48
+ }
49
+ /**
50
+ * One choice in a segmented button.
51
+ *
52
+ * It has no `size`, no `color` and no `variant` of its own: all three belong to
53
+ * the set, which is the only place they can be set once and mean the same thing
54
+ * for every segment.
55
+ */
56
+ export declare const Segment: React.ForwardRefExoticComponent<SegmentProps & React.RefAttributes<HTMLElement>>;
57
+ /**
58
+ * Two or more choices in one pill, exactly one of them taken.
59
+ *
60
+ * Underneath it is a radio group, and that is the whole accessibility argument:
61
+ * a segmented button *is* "exactly one of these", so it gets `role="radiogroup"`,
62
+ * one tab stop for the set, arrow keys within it, and `aria-checked` on the one
63
+ * that is taken. Building it out of `aria-pressed` toggles — which is what a row
64
+ * of buttons would give — would announce four independent switches, three of
65
+ * which happen to be off.
66
+ *
67
+ * The tile slides because its `left`, `top`, `width` and `height` are measured
68
+ * off the chosen segment and animated. Nothing is transformed: the tile is an
69
+ * empty box, and no label is resampled while it travels. That is the same
70
+ * distinction Tabs' indicator draws, and the reason the house no-transform rule
71
+ * survives a component whose entire point is that something moves.
72
+ *
73
+ * `left`, not `inset-inline-start`, for the reason Tabs gives: `offsetLeft` is a
74
+ * distance from the left edge and stays one under RTL. Pairing a physical
75
+ * measurement with a logical property is what would break the direction.
76
+ */
77
+ export declare const SegmentedButton: React.ForwardRefExoticComponent<SegmentedButtonProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as a from"react";import{Radio as n}from"@base-ui/react/radio";import{RadioGroup as r}from"@base-ui/react/radio-group";import{controlHeightClasses as s,controlSlots as l,controlTextClasses as o,gapClasses as i,hasContent as d,iconClasses as u,paddingXClasses as c,surfaceClasses as f,transitionClasses as h}from"../../internal/styles";const p=a.createContext({variant:"outline",size:"md",density:"default",fullWidth:!1}),b={solid:`${f} bg-(--n-panel) p-1 [box-shadow:var(--n-elev),var(--neba-plate-glass)]`,outline:`${f} border bg-(--n-panel) p-1 [border-color:var(--n-line)] [box-shadow:var(--n-elev),var(--neba-plate-glass)]`,text:""},m={solid:`${f} bg-(--n-fill) [box-shadow:var(--neba-shadow-1),var(--neba-plate-solid)]`,outline:`${f} bg-(--n-panel-press) [box-shadow:var(--neba-shadow-1),var(--neba-plate-glass)]`,text:`${f} bg-(--n-panel-press) [box-shadow:var(--neba-shadow-1),var(--neba-plate-glass)]`},v={solid:"data-[checked]:text-(--n-on-solid)",outline:"data-[checked]:text-(--n-accent)",text:"data-[checked]:text-(--n-accent)"};export const Segment=a.forwardRef(function({value:r,startIcon:l,endIcon:f,disabled:b=!1,className:m,children:x,...g},y){const{variant:w,size:k,density:$,fullWidth:R}=a.useContext(p);return t(n.Root,{ref:y,value:r,disabled:b,"data-segment":"",className:["relative z-10 inline-flex shrink-0 cursor-pointer items-center justify-center select-none","whitespace-nowrap font-medium","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",s[k],o[k],i[k],c[$][k],"rounded-full",h,u,"text-(--neba-muted-fg) hover:text-(--neba-fg)",v[w],"focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:[outline-offset:-2px]","data-[disabled]:cursor-not-allowed data-[disabled]:text-(--neba-disabled-fg)","data-[readonly]:cursor-default",R?"flex-1":"",m??""].filter(Boolean).join(" "),...g,children:[d(l)?e("span",{className:"flex h-[1lh] shrink-0 items-center",children:l}):null,x,d(f)?e("span",{className:"flex h-[1lh] shrink-0 items-center",children:f}):null]})});export const SegmentedButton=a.forwardRef(function({variant:n="outline",size:s="md",color:o="primary",density:i="default",elevation:d=0,value:u,defaultValue:c=null,onValueChange:f,disabled:v=!1,readOnly:x=!1,name:g,fullWidth:y=!1,className:w,style:k,children:$,...R},j){const[N,z]=a.useState(c),W=void 0!==u,C=W?u:N,P=a.useRef(null),S=a.useRef(null),A=a.useCallback(e=>{const t=P.current,a=S.current;if(!t||!a)return;const n=t.querySelector("[data-segment][data-checked]");if(!n)return;const r=!e||!a.hasAttribute("data-ready");r&&a.removeAttribute("data-ready"),a.style.setProperty("--n-seg-x",`${n.offsetLeft}px`),a.style.setProperty("--n-seg-y",`${n.offsetTop}px`),a.style.setProperty("--n-seg-w",`${n.offsetWidth}px`),a.style.setProperty("--n-seg-h",`${n.offsetHeight}px`),r&&a.offsetWidth,a.setAttribute("data-ready","")},[]);a.useLayoutEffect(()=>{A(!0)},[A,C,n,s,i,y,$]),a.useEffect(()=>{const e=P.current;if(!e)return;const t=new ResizeObserver(()=>A(!1));return t.observe(e),()=>t.disconnect()},[A]);const B=a.useMemo(()=>({variant:n,size:s,density:i,fullWidth:y}),[n,s,i,y]);return e(p.Provider,{value:B,children:t(r,{ref:e=>{P.current=e,"function"==typeof j?j(e):j&&(j.current=e)},value:C,onValueChange:e=>{const t=e??null;W||z(t),f?.(t)},disabled:v,readOnly:x,name:g,className:["relative inline-flex items-center rounded-full",b[n],h,x?"[filter:saturate(0.55)]":"",y?"flex w-full":"",w??""].filter(Boolean).join(" "),style:{...l(o,d,n),...k},...R,children:[null!=C?e("span",{ref:S,"aria-hidden":"true",className:["pointer-events-none absolute rounded-full","left-(--n-seg-x) top-(--n-seg-y) h-(--n-seg-h) w-(--n-seg-w)",m[n],"[transition-property:left,top,width,height]","[transition-timing-function:var(--neba-ease)]","[transition-duration:0ms] data-[ready]:[transition-duration:var(--neba-duration)]"].join(" ")}):null,$]})})});
@@ -0,0 +1,2 @@
1
+ export { Segment, SegmentedButton } from './SegmentedButton';
2
+ export type { SegmentedButtonProps, SegmentProps, SegmentValue } from './SegmentedButton';
@@ -0,0 +1 @@
1
+ export{Segment,SegmentedButton}from"./SegmentedButton";
@@ -0,0 +1,76 @@
1
+ import * as React from 'react';
2
+ import type { NebaElevation, NebaStyleProps } from '../../types';
3
+ /**
4
+ * What a Select's value may be.
5
+ *
6
+ * Deliberately not generic over arbitrary objects. A select is a form control,
7
+ * its value is what a form submits, and every escape from that — object values,
8
+ * custom equality, a stringifier for the trigger — buys flexibility by making
9
+ * the common case harder to write. Keep the identifier here and look the object
10
+ * up on the other side.
11
+ */
12
+ export type SelectValue = string | number;
13
+ export interface SelectOption {
14
+ /** Submitted, and what `value` / `onValueChange` speak in. */
15
+ value: SelectValue;
16
+ /** Shown in the list and in the trigger. Defaults to the value itself. */
17
+ label?: React.ReactNode;
18
+ /** Unavailable, but still listed — the option exists, it just cannot be picked. */
19
+ disabled?: boolean;
20
+ }
21
+ export interface SelectProps extends NebaStyleProps {
22
+ /**
23
+ * The options, as data. There is no `<Select.Option>` to compose: what a
24
+ * caller has is almost always an array already, and the list has to be
25
+ * available to the trigger before the popup has ever been opened.
26
+ */
27
+ items: readonly SelectOption[];
28
+ /** The chosen value. Use with `onValueChange` for a controlled select. */
29
+ value?: SelectValue | null;
30
+ /** The initially chosen value, for an uncontrolled select. */
31
+ defaultValue?: SelectValue | null;
32
+ onValueChange?: (value: SelectValue | null) => void;
33
+ /** Shown in the trigger while nothing is chosen. */
34
+ placeholder?: React.ReactNode;
35
+ /**
36
+ * Drop shadow depth of the *trigger*. The popup has its own, fixed: it
37
+ * genuinely floats above the page, which is the one case elevation is for.
38
+ * @default 0
39
+ */
40
+ elevation?: NebaElevation;
41
+ /** Label above the trigger, wired to it by Base UI's Field. */
42
+ label?: React.ReactNode;
43
+ /** Helper text below the trigger. */
44
+ description?: React.ReactNode;
45
+ /** Error message below. Its presence also turns the select invalid. */
46
+ error?: React.ReactNode;
47
+ /** Forces the invalid state without a message. Defaults to `!!error`. */
48
+ invalid?: boolean;
49
+ /** Content placed before the value. Sized in `em`, so it tracks the text. */
50
+ startIcon?: React.ReactNode;
51
+ /** Stretches to the width of the container. */
52
+ fullWidth?: boolean;
53
+ /** Unavailable. */
54
+ disabled?: boolean;
55
+ /** The value is shown but cannot be changed. */
56
+ readOnly?: boolean;
57
+ /** Whether a value must be chosen before the form is submitted. */
58
+ required?: boolean;
59
+ /** Identifies the field when a form is submitted. */
60
+ name?: string;
61
+ id?: string;
62
+ className?: string;
63
+ style?: React.CSSProperties;
64
+ }
65
+ /**
66
+ * One value chosen from a list of them.
67
+ *
68
+ * The trigger is a TextField's shell wearing a chevron, on purpose: a form where
69
+ * the select is a different height, radius or colour from the fields around it
70
+ * is a form that looks assembled rather than designed.
71
+ *
72
+ * Base UI owns everything hard about this — the popup's positioning and flipping,
73
+ * the focus trap, typeahead, the hidden input that makes it submit — and the
74
+ * work here is the surface it all wears.
75
+ */
76
+ export declare const Select: React.ForwardRefExoticComponent<SelectProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as t from"react";import{Select as l}from"@base-ui/react/select";import{Field as n}from"@base-ui/react/field";import{CheckIcon as r,ChevronIcon as i}from"../../internal/icons";import{WidthSizer as s}from"../../internal/sizer";import{controlHeightClasses as o,controlTextLeadingClasses as d,disabledClasses as c,fieldReadOnlyClasses as m,fieldRestClasses as u,focusWithinRingClasses as h,gapClasses as f,hasContent as p,iconClasses as b,metaTextClasses as g,paddingXClasses as x,radiusClasses as v,stackGapClasses as N,surfaceClasses as w,surfaceSlots as j,transitionClasses as y}from"../../internal/styles";const I=["group relative flex w-full cursor-pointer items-center select-none","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",y,"focus-within:[transition-duration:0ms]",h,b].join(" "),S=[w,"max-h-[min(20rem,var(--available-height))] overflow-y-auto overscroll-contain","min-w-[var(--anchor-width)] border bg-(--n-panel-press) p-1","[border-color:var(--n-line)]","[box-shadow:var(--neba-shadow-3),var(--neba-plate-glass)]","[outline:none]"].join(" "),$=["relative flex cursor-pointer items-center gap-2 select-none","rounded-(--neba-radius-xs) py-1.5 pe-2 ps-7",y,"data-[highlighted]:bg-(--n-soft-hover) data-[highlighted]:text-(--n-accent)","data-[selected]:text-(--n-accent) data-[selected]:font-medium","data-[disabled]:cursor-not-allowed data-[disabled]:text-(--neba-disabled-fg)","[outline:none]"].join(" ");export const Select=t.forwardRef(function({variant:h="outline",size:b="md",color:w="primary",density:y="default",elevation:V=0,items:z,value:k,defaultValue:O,onValueChange:P,placeholder:R,label:T,description:q,error:C,invalid:M,startIcon:W,fullWidth:_=!1,disabled:B=!1,readOnly:D=!1,required:E=!1,name:L,id:A,className:F,style:G},H){const J=null!=C&&!1!==C&&""!==C,K=M??J,Q=K?"danger":w,U=t.useMemo(()=>z.map(e=>({label:e.label??String(e.value),value:e.value})),[z]),X=t.useMemo(()=>[...z.map(e=>e.label??String(e.value)),...p(R)?[R]:[]],[z,R]);return a(n.Root,{disabled:B,invalid:K,className:["flex-col align-top",N[b],_?"flex w-full":"inline-flex",F??""].filter(Boolean).join(" "),style:{...j(Q,V),...G},children:[T?e(n.Label,{className:[g[b],"font-medium",B?"text-(--neba-disabled-fg)":"text-(--neba-fg)"].join(" "),children:T}):null,a(l.Root,{id:A,name:L,items:U,value:k,defaultValue:O,onValueChange:e=>P?.(e),disabled:B,readOnly:D,required:E,children:[a(l.Trigger,{ref:H,className:[I,o[b],d[b],v[b],f[b],x[y][b],B?c[h]:D?`${m[h]} cursor-default`:u[h]].join(" "),children:[W?e("span",{className:"flex h-[1lh] shrink-0 items-center text-(--neba-muted-fg)",children:W}):null,a("span",{className:"flex min-w-0 flex-1 flex-col",children:[e(l.Value,{className:["w-full truncate text-start","data-[placeholder]:text-(--neba-muted-fg)"].join(" "),placeholder:R}),e(s,{samples:X})]}),e(l.Icon,{className:["flex h-[1lh] shrink-0 items-center text-(--neba-muted-fg)","[transition:rotate_var(--neba-duration)_var(--neba-ease)]","data-[popup-open]:rotate-180"].join(" "),children:e(i,{})})]}),e(l.Portal,{children:e(l.Positioner,{className:"neba-portal z-50 [outline:none]",sideOffset:6,alignItemWithTrigger:!1,children:e(l.Popup,{className:`${S} ${v[b]} ${d[b]}`,style:j(Q,3),children:z.map(t=>a(l.Item,{value:t.value,disabled:t.disabled,className:$,children:[e(l.ItemIndicator,{className:"absolute start-1.5 flex size-4 items-center justify-center",children:e(r,{})}),e(l.ItemText,{className:"truncate",children:t.label??String(t.value)})]},String(t.value)))})})})]}),q?e(n.Description,{className:`${g[b]} text-(--neba-muted-fg)`,children:q}):null,J?e(n.Error,{match:!0,className:`${g[b]} text-(--n-accent)`,children:C}):null]})});
@@ -0,0 +1,2 @@
1
+ export { Select } from './Select';
2
+ export type { SelectOption, SelectProps, SelectValue } from './Select';
@@ -0,0 +1 @@
1
+ export{Select}from"./Select";
@@ -0,0 +1,60 @@
1
+ import * as React from 'react';
2
+ import type { NebaElevation, NebaStyleProps } from '../../types';
3
+ /**
4
+ * Which keyboard the shortcut is being read on.
5
+ *
6
+ * `auto` asks the browser, which is right for a shortcut a reader is about to
7
+ * press. The three explicit values are for documentation that has to name a
8
+ * platform rather than the reader's own — a support page describing the Windows
9
+ * build, a table comparing the two.
10
+ */
11
+ export type ShortcutOS = 'auto' | 'mac' | 'windows' | 'linux';
12
+ export interface ShortcutProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'span'>, 'color' | 'children'> {
13
+ /**
14
+ * The keys, innermost punctuation and all.
15
+ *
16
+ * A string is split on `+` — `'Mod+Shift+P'` — which covers everything except
17
+ * a shortcut whose key *is* a plus. For that one, pass the array form:
18
+ * `keys={['Ctrl', '+']}`.
19
+ */
20
+ keys: string | string[];
21
+ /**
22
+ * Which keyboard to name the modifiers for.
23
+ * @default 'auto'
24
+ */
25
+ os?: ShortcutOS;
26
+ /**
27
+ * What goes between two keys. Omit it for the platform's own convention: a `+`
28
+ * on Windows and Linux, and nothing at all on macOS, where a shortcut is
29
+ * written as a run of symbols — `⇧⌘P`, never `⇧+⌘+P`.
30
+ */
31
+ separator?: React.ReactNode;
32
+ /**
33
+ * Drop shadow depth. `0` (the default) is flat. A key cap is the one place a
34
+ * raised surface is tempting and wrong: this is a picture of a key, not a key.
35
+ * @default 0
36
+ */
37
+ elevation?: NebaElevation;
38
+ }
39
+ /**
40
+ * A keyboard key, or a combination of them.
41
+ *
42
+ * Two things make this more than a styled `<kbd>`, and both are about the label
43
+ * rather than the box around it.
44
+ *
45
+ * The first is `Mod`. A shortcut written as `Ctrl+K` is wrong for every Mac
46
+ * reader and one written as `⌘K` is wrong for everybody else, so the token that
47
+ * means "the modifier shortcuts are built on" resolves per platform — and `os`
48
+ * is there for the pages that have to name a platform rather than the reader's.
49
+ *
50
+ * The second is that `⌘` is not a word. A screen reader reads it as "place of
51
+ * interest sign", so every key drawn as a glyph carries its name beside it, in
52
+ * the clipped box `srOnlyClasses` describes. What is announced is "Command K",
53
+ * which is what the shortcut is called.
54
+ *
55
+ * The keys are real `<kbd>` elements; the wrapper around them is a `<span>`.
56
+ * Nesting `<kbd>` inside `<kbd>` is allowed and would also be defensible, but a
57
+ * `kbd` wrapper is a second box for a host stylesheet to reach into for no gain
58
+ * — the semantics are carried by the keys themselves either way.
59
+ */
60
+ export declare const Shortcut: React.ForwardRefExoticComponent<ShortcutProps & React.RefAttributes<HTMLSpanElement>>;
@@ -0,0 +1 @@
1
+ import{jsx as n,Fragment as e,jsxs as a}from"react/jsx-runtime";import*as o from"react";import{controlHeightClasses as t,controlSlots as r,controlTextClasses as l,paddingXClasses as s,radiusClasses as m,srOnlyClasses as i,surfaceClasses as c,transitionClasses as d}from"../../internal/styles";const w=n=>({symbol:n,name:n}),u={mod:{mac:{symbol:"⌘",name:"Command"},windows:w("Ctrl"),linux:w("Ctrl")},meta:{mac:{symbol:"⌘",name:"Command"},windows:w("Win"),linux:w("Super")},ctrl:{mac:{symbol:"⌃",name:"Control"},windows:w("Ctrl"),linux:w("Ctrl")},alt:{mac:{symbol:"⌥",name:"Option"},windows:w("Alt"),linux:w("Alt")},shift:{mac:{symbol:"⇧",name:"Shift"},windows:w("Shift"),linux:w("Shift")},enter:{mac:{symbol:"↩",name:"Enter"},windows:w("Enter"),linux:w("Enter")},tab:{mac:{symbol:"⇥",name:"Tab"},windows:w("Tab"),linux:w("Tab")},escape:{mac:{symbol:"⎋",name:"Escape"},windows:w("Esc"),linux:w("Esc")},backspace:{mac:{symbol:"⌫",name:"Backspace"},windows:w("Backspace"),linux:w("Backspace")},delete:{mac:{symbol:"⌦",name:"Delete"},windows:w("Del"),linux:w("Del")},capslock:{mac:{symbol:"⇪",name:"Caps Lock"},windows:w("Caps Lock"),linux:w("Caps Lock")}},p={cmdorctrl:"mod",commandorcontrol:"mod",cmd:"meta",command:"meta",super:"meta",win:"meta",windows:"meta",control:"ctrl",option:"alt",opt:"alt",return:"enter",esc:"escape",del:"delete",caps:"capslock"},b={up:{symbol:"↑",name:"Arrow up"},down:{symbol:"↓",name:"Arrow down"},left:{symbol:"←",name:"Arrow left"},right:{symbol:"→",name:"Arrow right"},arrowup:{symbol:"↑",name:"Arrow up"},arrowdown:{symbol:"↓",name:"Arrow down"},arrowleft:{symbol:"←",name:"Arrow left"},arrowright:{symbol:"→",name:"Arrow right"}};function f(){if("undefined"==typeof navigator)return"windows";const n=navigator.userAgentData,e=`${n?.platform??""} ${navigator.platform??""} ${navigator.userAgent??""}`;return/mac|iphone|ipad|ipod/i.test(e)?"mac":/win/i.test(e)?"windows":"linux"}function y(){return()=>{}}function x(){return"windows"}const g={xs:"xs",sm:"xs",md:"sm",lg:"md",xl:"lg"},h={solid:[c,"text-(--n-on-solid) bg-(--n-fill)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[c,"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)"},v={xs:"min-w-5.5",sm:"min-w-6.5",md:"min-w-8",lg:"min-w-10",xl:"min-w-12"};export const Shortcut=o.forwardRef(function({variant:c="outline",size:A="md",color:k="secondary",density:C="compact",elevation:j=0,keys:E,os:S="auto",separator:B,className:N,style:D,...L},T){const $=o.useSyncExternalStore(y,f,x),F="auto"===S?$:S,z=g[A],O=function(n){return Array.isArray(n)?n.map(n=>n.trim()).filter(Boolean):n.split("+").map(n=>n.trim()).filter(Boolean)}(E),R=O.map(n=>function(n,e){const a=n.toLowerCase().replace(/[\s_-]/g,""),o=p[a]??a,t=b[o];if(t)return t;const r=u[o];return r?r[e]:w(1===n.length?n.toUpperCase():n)}(n,F)),U=void 0===B?"mac"===F?null:"+":B,W=["inline-flex shrink-0 items-center justify-center","font-mono font-medium leading-none whitespace-nowrap tabular-nums",t[z],l[z],v[z],s[C][z],m[z],h[c],d].join(" ");return n("span",{ref:T,className:["inline-flex max-w-full items-center gap-1 align-middle",N??""].filter(Boolean).join(" "),style:{...r(k,j,c),...D},...L,children:R.map((t,r)=>a(o.Fragment,{children:[r>0&&null!==U?n("span",{"aria-hidden":"true",className:"text-(--neba-muted-fg)",children:U}):null,n("kbd",{className:W,children:t.symbol===t.name?t.symbol:a(e,{children:[n("span",{"aria-hidden":"true",children:t.symbol}),n("span",{className:i,children:t.name})]})})]},r))})});
@@ -0,0 +1,2 @@
1
+ export { Shortcut } from './Shortcut';
2
+ export type { ShortcutOS, ShortcutProps } from './Shortcut';
@@ -0,0 +1 @@
1
+ export{Shortcut}from"./Shortcut";
@@ -0,0 +1,38 @@
1
+ import * as React from 'react';
2
+ import { Slider as BaseUISlider } from '@base-ui/react/slider';
3
+ import type { NebaColor, NebaOrientation, NebaSize } from '../../types';
4
+ type BaseSliderProps = Omit<React.ComponentPropsWithoutRef<typeof BaseUISlider.Root>, 'className' | 'style' | 'render' | 'children' | 'orientation'>;
5
+ export interface SliderProps extends BaseSliderProps {
6
+ /** @default 'md' */
7
+ size?: NebaSize;
8
+ /** @default 'primary' */
9
+ color?: NebaColor;
10
+ /**
11
+ * Which way the slider runs. A vertical slider has no length of its own, so
12
+ * give it a height — the default `h-40` is a starting point, not a rule.
13
+ * @default 'horizontal'
14
+ */
15
+ orientation?: NebaOrientation;
16
+ /** The label above the track. */
17
+ label?: React.ReactNode;
18
+ /** Helper text below the track. */
19
+ description?: React.ReactNode;
20
+ /**
21
+ * Shows the current value beside the label. Pass a function to format it —
22
+ * the raw numbers and Base UI's already-localised strings are both handed in.
23
+ * @default false
24
+ */
25
+ showValue?: boolean | ((formatted: readonly string[], values: readonly number[]) => React.ReactNode);
26
+ /** Class names for the wrapper, not for the track. */
27
+ className?: string;
28
+ style?: React.CSSProperties;
29
+ }
30
+ /**
31
+ * A value chosen along a range.
32
+ *
33
+ * Pass an array to `value` or `defaultValue` and it becomes a range slider with
34
+ * one thumb per entry — there is no separate `range` prop, because the shape of
35
+ * the value already says which one this is.
36
+ */
37
+ export declare const Slider: React.ForwardRefExoticComponent<SliderProps & React.RefAttributes<HTMLDivElement>>;
38
+ export {};
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as l}from"react/jsx-runtime";import*as a from"react";import{Slider as r}from"@base-ui/react/slider";import{metaTextClasses as n,surfaceClasses as s,transitionClasses as o}from"../../internal/styles";const t={xs:"h-1",sm:"h-1.5",md:"h-1.5",lg:"h-2",xl:"h-2.5"},i={xs:"w-1",sm:"w-1.5",md:"w-1.5",lg:"w-2",xl:"w-2.5"},c={xs:"size-3",sm:"size-3.5",md:"size-4",lg:"size-5",xl:"size-6"},d={xs:"h-4",sm:"h-4.5",md:"h-5",lg:"h-6",xl:"h-7"},f={xs:"w-4",sm:"w-4.5",md:"w-5",lg:"w-6",xl:"w-7"},u="rounded-full bg-(--n-soft)",m=`rounded-full bg-(--n-fill) ${o}`,h=["rounded-full border bg-(--neba-surface)",s,"[border-color:var(--n-line-hover)]","[box-shadow:var(--neba-shadow-1),var(--neba-plate-glass)]","cursor-grab select-none active:cursor-grabbing",o,"hover:[box-shadow:var(--neba-shadow-2),0_0_0_4px_var(--n-soft)]","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-2","data-[dragging]:[box-shadow:var(--neba-shadow-1),0_0_0_6px_var(--n-soft-hover)]"].join(" ");export const Slider=a.forwardRef(function({size:a="md",color:s="primary",orientation:o="horizontal",label:b,description:x,showValue:v=!1,disabled:g=!1,className:w,style:p,...$},_){const y="vertical"===o,N={"--n-fill":`var(--neba-${s}-fill)`,"--n-accent":`var(--neba-${s}-accent)`,"--n-soft":`var(--neba-${s}-soft)`,"--n-soft-hover":`var(--neba-${s}-soft-hover)`,"--n-line-hover":`var(--neba-${s}-line-hover)`,"--n-ring":`var(--neba-${s}-ring)`},j=$.value??$.defaultValue,z=Array.isArray(j)?j.length:1;return l(r.Root,{ref:_,orientation:o,disabled:g,className:["flex",y?"w-fit flex-col items-center gap-2":"w-full flex-col gap-1.5",g?"[filter:saturate(0.25)] opacity-70 [&_*]:cursor-not-allowed":"",w??""].filter(Boolean).join(" "),style:{...N,...p},...$,children:[b||v?l("div",{className:`flex w-full items-baseline gap-2 ${n[a]}`,children:[b?e(r.Label,{className:g?"font-medium text-(--neba-disabled-fg)":"font-medium text-(--neba-fg)",children:b}):null,v?e(r.Value,{className:"ms-auto tabular-nums text-(--neba-muted-fg)",children:"function"==typeof v?v:null}):null]}):null,e(r.Control,{className:["flex touch-none select-none items-center justify-center",y?`${f[a]} h-40 flex-col`:`w-full ${d[a]}`].join(" "),children:l(r.Track,{className:[u,y?`${i[a]} h-full`:`${t[a]} w-full`].join(" "),children:[e(r.Indicator,{className:m}),Array.from({length:z},(l,n)=>e(r.Thumb,{index:n,className:`${h} ${c[a]}`},n))]})}),x?e("div",{className:`${n[a]} text-(--neba-muted-fg)`,children:x}):null]})});
@@ -0,0 +1,2 @@
1
+ export { Slider } from './Slider';
2
+ export type { SliderProps } from './Slider';
@@ -0,0 +1 @@
1
+ export{Slider}from"./Slider";
@@ -0,0 +1,91 @@
1
+ import * as React from 'react';
2
+ import { type BoxProps } from '../box/Box';
3
+ import type { NebaAlign } from '../../types';
4
+ export interface StatisticProps extends Omit<BoxProps, 'title' | 'prefix'> {
5
+ /**
6
+ * What the number is. The name of the measure, not a sentence about it —
7
+ * "Active users", "MRR", "이탈률".
8
+ *
9
+ * `label` rather than `title`, which is what Card calls the same slot: this is
10
+ * the name of a *value*, which is the thing the library already spells `label`
11
+ * on every field it has.
12
+ */
13
+ label?: React.ReactNode;
14
+ /**
15
+ * The figure. A number is formatted; a string is printed exactly as given, for
16
+ * the values that are not numbers at all — "3h 42m", "A+", "—".
17
+ */
18
+ value: number | string;
19
+ /**
20
+ * How to write a numeric `value` — `Intl.NumberFormat` options, the same prop
21
+ * the progress indicators take. Without it a number is grouped by the reader's
22
+ * own locale and otherwise left alone.
23
+ */
24
+ format?: Intl.NumberFormatOptions;
25
+ /** Set before the figure and never wrapped away from it — a currency sign. */
26
+ prefix?: React.ReactNode;
27
+ /**
28
+ * Set after the figure: `%`, `MB`, `명`.
29
+ *
30
+ * A second slot rather than one `adornment` with a side, because the two are
31
+ * typographically different things and always have been — a currency symbol
32
+ * leads its number and a unit follows it, in every locale that has both.
33
+ */
34
+ unit?: React.ReactNode;
35
+ /** A glyph before the label. An `Icon`, or anything else that draws. */
36
+ icon?: React.ReactNode;
37
+ /**
38
+ * The figure this one is being compared against — last month's, last quarter's,
39
+ * the target. Passing it is what makes the delta appear.
40
+ *
41
+ * Only meaningful for a numeric `value`; with a string there is nothing to
42
+ * subtract and the delta is left out.
43
+ */
44
+ previousValue?: number;
45
+ /**
46
+ * How the difference is written.
47
+ *
48
+ * - `percent` — the change relative to `previousValue`. The default, because a
49
+ * report is nearly always asking "how much has this moved", not "by how many".
50
+ * - `absolute` — the difference itself, in the figure's own units.
51
+ * - `both` — the percentage with the difference after it.
52
+ * - `none` — no delta, even with a `previousValue` given.
53
+ * @default 'percent'
54
+ */
55
+ delta?: 'percent' | 'absolute' | 'both' | 'none';
56
+ /**
57
+ * Which direction counts as good, and so which way the delta is coloured.
58
+ *
59
+ * `up` for revenue, `down` for churn and error rate and page weight. This is
60
+ * not decoration: green-for-larger on a bounce rate says the opposite of what
61
+ * the report means, and it says it to exactly the reader who is skimming.
62
+ * @default 'up'
63
+ */
64
+ betterWhen?: 'up' | 'down';
65
+ /** A line under the figure — "vs. last month", "as of 3 July". */
66
+ caption?: React.ReactNode;
67
+ /**
68
+ * Where the block sits in the card. `center` for a row of tiles that read as
69
+ * one band; `start` — the default — for a card with anything else in it.
70
+ * @default 'start'
71
+ */
72
+ align?: NebaAlign;
73
+ /** Anything below the caption: a sparkline, a ProgressLinear against a target. */
74
+ children?: React.ReactNode;
75
+ }
76
+ /**
77
+ * A number with its name on it, and — when there is something to compare it
78
+ * against — how far it has moved.
79
+ *
80
+ * A Box with a fixed arrangement laid on it, exactly as Card is. The slots are
81
+ * props and not compound sub-components for the reason Card gives: the order
82
+ * never varies, so what a caller wants to decide is what goes in each slot.
83
+ *
84
+ * The delta is a Chip rather than a coloured span, and that is the whole reason
85
+ * it looks right next to everything else — it is the same token the rest of the
86
+ * library uses, at the same step down the control ladder, with the same acrylic
87
+ * on it. It also carries a shape and not only a colour: a report whose "down" is
88
+ * red and nothing else is a report that says nothing to a reader who cannot
89
+ * separate red from green.
90
+ */
91
+ export declare const Statistic: React.ForwardRefExoticComponent<StatisticProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as l from"react";import{Box as n}from"../box/Box";import{Chip as a}from"../chip/Chip";import{MinusIcon as r,TrendDownIcon as s,TrendUpIcon as m}from"../../internal/icons";import{hasContent as i,metaTextClasses as o,sheetSectionGapClasses as c}from"../../internal/styles";const u={xs:"text-[1.125rem]/[1.375rem]",sm:"text-[1.375rem]/[1.625rem]",md:"text-[1.75rem]/[2rem]",lg:"text-[2.25rem]/[2.5rem]",xl:"text-[2.875rem]/[3.25rem]"},d={xs:"text-[0.75rem]",sm:"text-[0.875rem]",md:"text-[1rem]",lg:"text-[1.25rem]",xl:"text-[1.5rem]"},x={xs:"gap-y-0.5",sm:"gap-y-0.5",md:"gap-y-1",lg:"gap-y-1",xl:"gap-y-1.5"},f={start:"items-start text-start",center:"items-center text-center",end:"items-end text-end"},p={up:e(m,{}),down:e(s,{}),flat:e(r,{})};export const Statistic=l.forwardRef(function({size:r="md",color:s="primary",density:m="default",label:b,value:h,format:g,prefix:w,unit:N,icon:y,previousValue:v,delta:$="percent",betterWhen:j="up",caption:M,align:z="start",className:B,children:k,...F},I){const C=l.useMemo(()=>new Intl.NumberFormat(void 0,g),[g]),R="number"==typeof h?h:null,S=null===R?h:C.format(R),V=null!==R&&void 0!==v?R-v:null,W=null!==V&&0!==v&&void 0!==v?V/Math.abs(v):null,q=null===V?null:V>0?"up":V<0?"down":"flat";let A=null;if(null!==V&&null!==q&&"none"!==$){const e=V>0?"+":V<0?"-":"",t=`${e}${C.format(Math.abs(V))}`,l=null===W?null:`${e}${(100*Math.abs(W)).toFixed(1).replace(/\.0$/,"")}%`;A="absolute"===$?t:"both"===$?[l,t].filter(Boolean).join(" · "):l??t}const D=null===q||"flat"===q?"secondary":q===j?"success":"danger",E=null!==A||i(M);return t(n,{ref:I,size:r,color:s,density:m,className:["flex flex-col",c[r],f[z],B??""].filter(Boolean).join(" "),...F,children:[i(b)||i(y)?t("div",{className:["flex min-w-0 items-center gap-1.5 font-medium text-(--neba-muted-fg)",o[r]].join(" "),children:[i(y)?e("span",{className:"flex shrink-0 items-center",children:y}):null,i(b)?e("span",{className:"min-w-0 truncate",children:b}):null]}):null,t("div",{className:["flex min-w-0 flex-wrap items-baseline gap-1 font-semibold text-(--neba-fg) tabular-nums",u[r]].join(" "),children:[i(w)?e("span",{className:`font-medium text-(--neba-muted-fg) ${d[r]}`,children:w}):null,e("span",{className:"min-w-0 break-words",children:S}),i(N)?e("span",{className:`font-medium text-(--neba-muted-fg) ${d[r]}`,children:N}):null]}),E?t("div",{className:`flex min-w-0 flex-wrap items-center gap-x-2 ${x[r]}`,children:[null!==A&&null!==q?e(a,{size:r,variant:"text",color:D,startIcon:p[q],className:"tabular-nums",children:A}):null,i(M)?e("span",{className:`min-w-0 text-(--neba-muted-fg) ${o[r]}`,children:M}):null]}):null,k]})});
@@ -0,0 +1,2 @@
1
+ export { Statistic } from './Statistic';
2
+ export type { StatisticProps } from './Statistic';