neba 0.0.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -2
- package/dist/components/accordion/Accordion.d.ts +97 -0
- package/dist/components/accordion/Accordion.js +1 -0
- package/dist/components/accordion/index.d.ts +2 -0
- package/dist/components/accordion/index.js +1 -0
- package/dist/components/alert/Alert.d.ts +47 -0
- package/dist/components/alert/Alert.js +1 -0
- package/dist/components/alert/index.d.ts +2 -0
- package/dist/components/alert/index.js +1 -0
- package/dist/components/badge/Badge.d.ts +80 -0
- package/dist/components/badge/Badge.js +1 -0
- package/dist/components/badge/index.d.ts +2 -0
- package/dist/components/badge/index.js +1 -0
- package/dist/components/box/Box.d.ts +50 -0
- package/dist/components/box/Box.js +1 -0
- package/dist/components/box/index.d.ts +2 -0
- package/dist/components/box/index.js +1 -0
- package/dist/components/button/Button.d.ts +25 -3
- package/dist/components/button/Button.js +1 -1
- package/dist/components/button/index.d.ts +1 -0
- package/dist/components/button-group/ButtonGroup.d.ts +31 -0
- package/dist/components/button-group/ButtonGroup.js +1 -0
- package/dist/components/button-group/index.d.ts +2 -0
- package/dist/components/button-group/index.js +1 -0
- package/dist/components/card/Card.d.ts +45 -0
- package/dist/components/card/Card.js +1 -0
- package/dist/components/card/index.d.ts +2 -0
- package/dist/components/card/index.js +1 -0
- package/dist/components/checkbox/Checkbox.d.ts +39 -0
- package/dist/components/checkbox/Checkbox.js +1 -0
- package/dist/components/checkbox/index.d.ts +2 -0
- package/dist/components/checkbox/index.js +1 -0
- package/dist/components/chip/Chip.d.ts +50 -0
- package/dist/components/chip/Chip.js +1 -0
- package/dist/components/chip/index.d.ts +2 -0
- package/dist/components/chip/index.js +1 -0
- package/dist/components/combobox/Combobox.d.ts +131 -0
- package/dist/components/combobox/Combobox.js +1 -0
- package/dist/components/combobox/index.d.ts +2 -0
- package/dist/components/combobox/index.js +1 -0
- package/dist/components/dialog/Dialog.d.ts +111 -0
- package/dist/components/dialog/Dialog.js +1 -0
- package/dist/components/dialog/index.d.ts +2 -0
- package/dist/components/dialog/index.js +1 -0
- package/dist/components/divider/Divider.d.ts +48 -0
- package/dist/components/divider/Divider.js +1 -0
- package/dist/components/divider/index.d.ts +2 -0
- package/dist/components/divider/index.js +1 -0
- package/dist/components/file-picker/FilePicker.d.ts +103 -0
- package/dist/components/file-picker/FilePicker.js +1 -0
- package/dist/components/file-picker/index.d.ts +2 -0
- package/dist/components/file-picker/index.js +1 -0
- package/dist/components/list/List.d.ts +81 -0
- package/dist/components/list/List.js +1 -0
- package/dist/components/list/index.d.ts +2 -0
- package/dist/components/list/index.js +1 -0
- package/dist/components/menu/Menu.d.ts +216 -0
- package/dist/components/menu/Menu.js +1 -0
- package/dist/components/menu/index.d.ts +2 -0
- package/dist/components/menu/index.js +1 -0
- package/dist/components/number-field/NumberField.d.ts +113 -0
- package/dist/components/number-field/NumberField.js +1 -0
- package/dist/components/number-field/index.d.ts +2 -0
- package/dist/components/number-field/index.js +1 -0
- package/dist/components/overlay/Overlay.d.ts +85 -0
- package/dist/components/overlay/Overlay.js +1 -0
- package/dist/components/overlay/index.d.ts +2 -0
- package/dist/components/overlay/index.js +1 -0
- package/dist/components/pagination/Pagination.d.ts +65 -0
- package/dist/components/pagination/Pagination.js +1 -0
- package/dist/components/pagination/index.d.ts +2 -0
- package/dist/components/pagination/index.js +1 -0
- package/dist/components/progress-box/ProgressBox.d.ts +33 -0
- package/dist/components/progress-box/ProgressBox.js +1 -0
- package/dist/components/progress-box/index.d.ts +2 -0
- package/dist/components/progress-box/index.js +1 -0
- package/dist/components/progress-circular/ProgressCircular.d.ts +19 -0
- package/dist/components/progress-circular/ProgressCircular.js +1 -0
- package/dist/components/progress-circular/index.d.ts +2 -0
- package/dist/components/progress-circular/index.js +1 -0
- package/dist/components/progress-linear/ProgressLinear.d.ts +20 -0
- package/dist/components/progress-linear/ProgressLinear.js +1 -0
- package/dist/components/progress-linear/index.d.ts +2 -0
- package/dist/components/progress-linear/index.js +1 -0
- package/dist/components/radio-group/RadioGroup.d.ts +51 -0
- package/dist/components/radio-group/RadioGroup.js +1 -0
- package/dist/components/radio-group/index.d.ts +2 -0
- package/dist/components/radio-group/index.js +1 -0
- package/dist/components/select/Select.d.ts +76 -0
- package/dist/components/select/Select.js +1 -0
- package/dist/components/select/index.d.ts +2 -0
- package/dist/components/select/index.js +1 -0
- package/dist/components/slider/Slider.d.ts +38 -0
- package/dist/components/slider/Slider.js +1 -0
- package/dist/components/slider/index.d.ts +2 -0
- package/dist/components/slider/index.js +1 -0
- package/dist/components/switch/Switch.d.ts +39 -0
- package/dist/components/switch/Switch.js +1 -0
- package/dist/components/switch/index.d.ts +2 -0
- package/dist/components/switch/index.js +1 -0
- package/dist/components/table/Table.d.ts +80 -0
- package/dist/components/table/Table.js +1 -0
- package/dist/components/table/index.d.ts +2 -0
- package/dist/components/table/index.js +1 -0
- package/dist/components/tabs/Tabs.d.ts +92 -0
- package/dist/components/tabs/Tabs.js +1 -0
- package/dist/components/tabs/index.d.ts +2 -0
- package/dist/components/tabs/index.js +1 -0
- package/dist/components/text-field/TextField.d.ts +63 -0
- package/dist/components/text-field/TextField.js +1 -0
- package/dist/components/text-field/index.d.ts +2 -0
- package/dist/components/text-field/index.js +1 -0
- package/dist/components/toast/Toast.d.ts +116 -0
- package/dist/components/toast/Toast.js +1 -0
- package/dist/components/toast/index.d.ts +2 -0
- package/dist/components/toast/index.js +1 -0
- package/dist/components/tooltip/Tooltip.d.ts +81 -0
- package/dist/components/tooltip/Tooltip.js +1 -0
- package/dist/components/tooltip/index.d.ts +2 -0
- package/dist/components/tooltip/index.js +1 -0
- package/dist/components/typography/Typography.d.ts +62 -0
- package/dist/components/typography/Typography.js +1 -0
- package/dist/components/typography/index.d.ts +2 -0
- package/dist/components/typography/index.js +1 -0
- package/dist/index.d.ts +32 -1
- package/dist/index.js +1 -1
- package/dist/internal/button-group.d.ts +25 -0
- package/dist/internal/button-group.js +1 -0
- package/dist/internal/icons.d.ts +48 -0
- package/dist/internal/icons.js +1 -0
- package/dist/internal/menu.d.ts +20 -0
- package/dist/internal/menu.js +1 -0
- package/dist/internal/progress.d.ts +122 -0
- package/dist/internal/progress.js +1 -0
- package/dist/internal/styles.d.ts +224 -0
- package/dist/internal/styles.js +1 -0
- package/dist/styles.css +758 -0
- package/dist/types.d.ts +77 -0
- package/dist/types.js +8 -0
- package/package.json +75 -19
- package/pnpm-workspace.yaml +0 -2
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type BoxProps } from '../box/Box';
|
|
3
|
+
export interface CardProps extends Omit<BoxProps, 'title' | 'padded'> {
|
|
4
|
+
/**
|
|
5
|
+
* The card's heading. A plain string is styled as the title; pass a real
|
|
6
|
+
* heading element (`title={<h2>…</h2>}`) when the card needs to appear in the
|
|
7
|
+
* document outline — it inherits the title's typography rather than the
|
|
8
|
+
* browser's.
|
|
9
|
+
*/
|
|
10
|
+
title?: React.ReactNode;
|
|
11
|
+
/** A second line under the title, one step down the type scale and muted. */
|
|
12
|
+
subtitle?: React.ReactNode;
|
|
13
|
+
/**
|
|
14
|
+
* Content pinned to the end of the header row — a menu button, a status chip.
|
|
15
|
+
* Stays on the title's line while the title wraps beside it.
|
|
16
|
+
*/
|
|
17
|
+
headerAction?: React.ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* The bottom area. Laid out as a wrapping row so a pair of buttons needs no
|
|
20
|
+
* wrapper of its own; anything else can bring its own layout.
|
|
21
|
+
*/
|
|
22
|
+
footer?: React.ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Draws a hairline between the sections instead of separating them with
|
|
25
|
+
* space. The lines run the full width of the sheet, so the padding moves from
|
|
26
|
+
* the card onto each section.
|
|
27
|
+
* @default false
|
|
28
|
+
*/
|
|
29
|
+
dividers?: boolean;
|
|
30
|
+
/** The card's body. */
|
|
31
|
+
children?: React.ReactNode;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A Box with the parts a card is made of laid out on it: a title, a subtitle,
|
|
35
|
+
* a body and a footer.
|
|
36
|
+
*
|
|
37
|
+
* The sections are props rather than compound sub-components — `<Card.Header>`,
|
|
38
|
+
* `<Card.Title>` — for the same reason TextField takes `label` and `description`
|
|
39
|
+
* as props: the arrangement is fixed, and the thing a caller wants to decide is
|
|
40
|
+
* what goes in each slot, not what order the slots come in.
|
|
41
|
+
*
|
|
42
|
+
* Every Box prop passes straight through, so a card is styled on exactly the
|
|
43
|
+
* same axes as the box it is.
|
|
44
|
+
*/
|
|
45
|
+
export declare const Card: React.ForwardRefExoticComponent<CardProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as l,Fragment as t}from"react/jsx-runtime";import*as n from"react";import{Box as a,boxPaddingXClasses as r,boxPaddingYClasses as s}from"../box/Box";import{hasContent as i,metaTextClasses as o,sheetBodyClasses as c,sheetHeaderGapClasses as d,sheetSectionGapClasses as m,sheetTitleClasses as f}from"../../internal/styles";export const Card=n.forwardRef(function({size:n="md",density:u="default",title:x,subtitle:h,headerAction:N,footer:p,dividers:$=!1,className:b,children:v,...y},j){const k=r[u][n],g=s[u][n],w=$?`${k} ${g}`:k,B=i(x)||i(h)||i(N),z=l(t,{children:[i(x)||i(h)?l("div",{className:`flex min-w-0 flex-1 flex-col ${d[n]}`,children:[i(x)?e("div",{className:`neba-title font-semibold ${f[n]}`,children:x}):null,i(h)?e("div",{className:`text-(--neba-muted-fg) ${o[n]}`,children:h}):null]}):null,i(N)?e("div",{className:"ml-auto shrink-0",children:N}):null]}),A=[B?{key:"header",className:"flex items-start gap-3",content:z}:null,i(v)?{key:"content",className:c[n],content:v}:null,i(p)?{key:"footer",className:`flex flex-wrap items-center gap-2 ${c[n]}`,content:p}:null].filter(e=>null!==e);return e(a,{ref:j,size:n,density:u,padded:!1,className:["flex flex-col",$?"":`${g} ${m[n]}`,b??""].filter(Boolean).join(" "),...y,children:A.map((l,t)=>e("div",{className:[w,l.className,$&&t>0?"border-t [border-color:var(--n-line)]":""].filter(Boolean).join(" "),children:l.content},l.key))})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Card}from"./Card";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Checkbox as BaseUICheckbox } from '@base-ui/react/checkbox';
|
|
3
|
+
import type { NebaColor, NebaSize } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* Base UI's own props, minus the ones this component owns: `className` and
|
|
6
|
+
* `style` land on the field wrapper rather than on the tick, and `render` would
|
|
7
|
+
* replace the tick with something that is no longer a checkbox.
|
|
8
|
+
*/
|
|
9
|
+
type BaseCheckboxProps = Omit<React.ComponentPropsWithoutRef<typeof BaseUICheckbox.Root>, 'className' | 'style' | 'render' | 'children'>;
|
|
10
|
+
export interface CheckboxProps extends BaseCheckboxProps {
|
|
11
|
+
/** @default 'md' */
|
|
12
|
+
size?: NebaSize;
|
|
13
|
+
/** @default 'primary' */
|
|
14
|
+
color?: NebaColor;
|
|
15
|
+
/** The text beside the tick. Wired to it by Base UI's Field. */
|
|
16
|
+
label?: React.ReactNode;
|
|
17
|
+
/** Helper text under the label. */
|
|
18
|
+
description?: React.ReactNode;
|
|
19
|
+
/** Error message below. Its presence also turns the checkbox invalid. */
|
|
20
|
+
error?: React.ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* Forces the invalid state without a message — for when an external form
|
|
23
|
+
* library owns the validity. Defaults to whether `error` has content.
|
|
24
|
+
*/
|
|
25
|
+
invalid?: boolean;
|
|
26
|
+
/** Class names for the field wrapper, not for the tick itself. */
|
|
27
|
+
className?: string;
|
|
28
|
+
style?: React.CSSProperties;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A single yes/no, or one member of a set of them.
|
|
32
|
+
*
|
|
33
|
+
* `label`, `description` and `error` are props rather than children for the same
|
|
34
|
+
* reason they are on TextField: the arrangement is fixed and what a caller wants
|
|
35
|
+
* to decide is what goes in each slot. `children` is not accepted at all —
|
|
36
|
+
* anything a checkbox has to say belongs in one of the three.
|
|
37
|
+
*/
|
|
38
|
+
export declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLElement>>;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as n from"react";import{Checkbox as r}from"@base-ui/react/checkbox";import{Field as l}from"@base-ui/react/field";import{controlTextClasses as i,metaTextClasses as o,surfaceClasses as t,tickRadiusClasses as s,tickSizeClasses as d,transitionClasses as c}from"../../internal/styles";const b=["relative inline-flex shrink-0 items-center justify-center border","[-webkit-tap-highlight-color:transparent] [touch-action:manipulation]",c,"active:[transition-duration:0ms]","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-2"].join(" "),h=[t,"cursor-pointer bg-(--n-panel) [border-color:var(--n-line)]","[box-shadow:var(--neba-plate-glass)]","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]:[box-shadow:var(--neba-plate-solid)]","data-[checked]:hover:bg-(--n-fill-hover)","data-[indeterminate]:bg-(--n-fill) data-[indeterminate]:text-(--n-on-solid)","data-[indeterminate]:[border-color:transparent]"].join(" "),f=[t,"cursor-default bg-(--n-panel) [border-color:var(--n-line)]","[box-shadow:var(--neba-plate-glass)] [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)]","text-(--neba-disabled-fg) shadow-none","data-[checked]:bg-(--neba-disabled-bg)","data-[indeterminate]:bg-(--neba-disabled-bg)"].join(" ");function m(){return e("svg",{viewBox:"0 0 12 12",fill:"none","aria-hidden":"true",className:"size-full",children:e("path",{d:"M2 6.2 4.6 8.8 10 3.4",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})}function p(){return e("svg",{viewBox:"0 0 12 12",fill:"none","aria-hidden":"true",className:"size-full",children:e("path",{d:"M2.5 6h7",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"})})}export const Checkbox=n.forwardRef(function({size:n="md",color:t="primary",label:c,description:u,error:g,invalid:x,disabled:k=!1,readOnly:j=!1,className:N,style:w,...$},y){const z=null!=g&&!1!==g&&""!==g,C=x??z,L=C?"danger":t,B={"--n-fill":`var(--neba-${L}-fill)`,"--n-fill-hover":`var(--neba-${L}-fill-hover)`,"--n-on-solid":`var(--neba-${L}-on-solid)`,"--n-accent":`var(--neba-${L}-accent)`,"--n-panel":"var(--neba-panel)","--n-panel-hover":"var(--neba-panel-hover)","--n-line":`var(--neba-${L}-line)`,"--n-line-hover":`var(--neba-${L}-line-hover)`,"--n-ring":`var(--neba-${L}-ring)`},R=[b,d[n],s[n],k?v:j?f:h].join(" ");return a(l.Root,{disabled:k,invalid:C,className:["inline-flex flex-col gap-1 align-top",N??""].filter(Boolean).join(" "),style:{...B,...w},children:[a("div",{className:`flex items-start gap-2 ${i[n]}`,children:[e("span",{className:"flex h-[1lh] shrink-0 items-center",children:e(r.Root,{ref:y,className:R,disabled:k,readOnly:j,...$,children:e(r.Indicator,{className:"flex size-[70%] items-center justify-center",children:$.indeterminate?e(p,{}):e(m,{})})})}),c||u?a("span",{className:"flex min-w-0 flex-col gap-0.5",children:[c?e(l.Label,{className:["leading-[1.4]",k?"text-(--neba-disabled-fg)":"cursor-pointer text-(--neba-fg)"].join(" "),children:c}):null,u?e(l.Description,{className:`${o[n]} text-(--neba-muted-fg)`,children:u}):null]}):null]}),z?e(l.Error,{match:!0,className:`${o[n]} text-(--n-accent)`,children:g}):null]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Checkbox}from"./Checkbox";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
export interface ChipProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'span'>, 'color'> {
|
|
4
|
+
/**
|
|
5
|
+
* Drop shadow depth. `0` (the default) is flat. A chip is a token sitting on
|
|
6
|
+
* something else, so this is raised even less often than on a Button.
|
|
7
|
+
* @default 0
|
|
8
|
+
*/
|
|
9
|
+
elevation?: NebaElevation;
|
|
10
|
+
/** Content placed before the label — an icon, a status dot, an avatar. */
|
|
11
|
+
startIcon?: React.ReactNode;
|
|
12
|
+
/** Content placed after the label, before any `count`. */
|
|
13
|
+
endIcon?: React.ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* A number set into the end of the chip. Rendered on its own small plate, so
|
|
16
|
+
* "Errors 12" reads as one token with a count rather than as two words.
|
|
17
|
+
*/
|
|
18
|
+
count?: React.ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Called when the chip's delete affordance is pressed. Passing it is what
|
|
21
|
+
* makes the affordance appear.
|
|
22
|
+
*/
|
|
23
|
+
onDelete?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
24
|
+
/** Accessible name of the delete button. */
|
|
25
|
+
deleteLabel?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Marks the chip as chosen — a filter that is on. `selected` deepens the
|
|
28
|
+
* surface a step rather than changing the colour family, so a row of chips
|
|
29
|
+
* stays one row of chips.
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
selected?: boolean;
|
|
33
|
+
/** Unavailable. Drops the colour family for neutral grey, as everywhere else. */
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
children?: React.ReactNode;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A compact token: a tag, a filter, a status, an entity plucked out of a list.
|
|
39
|
+
*
|
|
40
|
+
* The shell is always a `<span>`. What changes is what is inside it: a plain run
|
|
41
|
+
* of content, or — when `onClick` is given — a real `<button>` wrapping that
|
|
42
|
+
* content, plus a second button for `onDelete`. Both are reachable by keyboard,
|
|
43
|
+
* and neither is nested inside the other.
|
|
44
|
+
*
|
|
45
|
+
* An inert `<span>` carrying a click handler is the single most common way a
|
|
46
|
+
* component library loses its keyboard users, and a `<button>` inside a
|
|
47
|
+
* `<button>` is the most common way it invents a chip that Chrome silently
|
|
48
|
+
* rewrites. This shape is what avoids both.
|
|
49
|
+
*/
|
|
50
|
+
export declare const Chip: React.ForwardRefExoticComponent<ChipProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +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]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Chip}from"./Chip";
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* What a Combobox's value may be — the same two types a [Select](../select)
|
|
5
|
+
* submits, and for the same reason: a form control's value is what a form
|
|
6
|
+
* sends, and every escape from that buys flexibility by making the common case
|
|
7
|
+
* harder to write.
|
|
8
|
+
*
|
|
9
|
+
* A value the list does not contain is a `string`: it is what the user typed.
|
|
10
|
+
*/
|
|
11
|
+
export type ComboboxValue = string | number;
|
|
12
|
+
export interface ComboboxOption {
|
|
13
|
+
/** Submitted, and what `value` / `onValueChange` speak in. */
|
|
14
|
+
value: ComboboxValue;
|
|
15
|
+
/**
|
|
16
|
+
* Shown in the list, in the input and on the chip. Defaults to the value.
|
|
17
|
+
*
|
|
18
|
+
* A `string` rather than a `ReactNode`, which is the one place this differs
|
|
19
|
+
* from Select: the label is typed against by the filter and written into a
|
|
20
|
+
* text input, and neither of those can be done to an element.
|
|
21
|
+
*/
|
|
22
|
+
label?: string;
|
|
23
|
+
/** Unavailable, but still listed — the option exists, it just cannot be picked. */
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** One value, or an array of them, depending on `multiple`. */
|
|
27
|
+
type Selection<Multiple extends boolean | undefined> = Multiple extends true ? ComboboxValue[] : ComboboxValue | null;
|
|
28
|
+
export interface ComboboxProps<Multiple extends boolean | undefined = false> extends NebaStyleProps {
|
|
29
|
+
/**
|
|
30
|
+
* The options, as data — the same shape Select takes, and for the same
|
|
31
|
+
* reason: what a caller has is almost always an array already.
|
|
32
|
+
*/
|
|
33
|
+
items: readonly ComboboxOption[];
|
|
34
|
+
/**
|
|
35
|
+
* Whether more than one value may be held. The chosen ones become chips
|
|
36
|
+
* inside the field, and the input goes on filtering after each.
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
multiple?: Multiple;
|
|
40
|
+
/** The chosen value. Use with `onValueChange` for a controlled combobox. */
|
|
41
|
+
value?: Selection<Multiple> | null;
|
|
42
|
+
/** The initially chosen value, for an uncontrolled combobox. */
|
|
43
|
+
defaultValue?: Selection<Multiple> | null;
|
|
44
|
+
onValueChange?: (value: Selection<Multiple>) => void;
|
|
45
|
+
/** Called as the text in the input changes — the filter query, not the value. */
|
|
46
|
+
onInputValueChange?: (inputValue: string) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Whether a value the list does not contain may be committed.
|
|
49
|
+
*
|
|
50
|
+
* On by default, and it is what separates this from a searchable Select: the
|
|
51
|
+
* typed text is offered as its own row at the end of the list, so committing
|
|
52
|
+
* it is a choice the user makes rather than something that happens to them on
|
|
53
|
+
* blur. Turn it off for a field whose values are a closed set.
|
|
54
|
+
* @default true
|
|
55
|
+
*/
|
|
56
|
+
allowCustom?: boolean;
|
|
57
|
+
/** What that row says. Receives the trimmed query. */
|
|
58
|
+
customLabel?: (query: string) => React.ReactNode;
|
|
59
|
+
/**
|
|
60
|
+
* Shows a × that empties the field. Off by default — a field that can be
|
|
61
|
+
* cleared in one click is a field that can be emptied by accident.
|
|
62
|
+
* @default false
|
|
63
|
+
*/
|
|
64
|
+
clearable?: boolean;
|
|
65
|
+
/** Shown in the popup when nothing matches and no value may be added. */
|
|
66
|
+
emptyMessage?: React.ReactNode;
|
|
67
|
+
/** The most rows the list will show at once. `-1` is all of them. @default -1 */
|
|
68
|
+
limit?: number;
|
|
69
|
+
/** Shown in the input while nothing is typed. */
|
|
70
|
+
placeholder?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Drop shadow depth of the *field*. The popup has its own, fixed: it
|
|
73
|
+
* genuinely floats above the page, which is the one case elevation is for.
|
|
74
|
+
* @default 0
|
|
75
|
+
*/
|
|
76
|
+
elevation?: NebaElevation;
|
|
77
|
+
/** Label above the field, wired to it by Base UI's Field. */
|
|
78
|
+
label?: React.ReactNode;
|
|
79
|
+
/** Helper text below the field. */
|
|
80
|
+
description?: React.ReactNode;
|
|
81
|
+
/** Error message below. Its presence also turns the combobox invalid. */
|
|
82
|
+
error?: React.ReactNode;
|
|
83
|
+
/** Forces the invalid state without a message. Defaults to `!!error`. */
|
|
84
|
+
invalid?: boolean;
|
|
85
|
+
/** Content placed before the input. Sized in `em`, so it tracks the text. */
|
|
86
|
+
startIcon?: React.ReactNode;
|
|
87
|
+
/** Stretches to the width of the container. */
|
|
88
|
+
fullWidth?: boolean;
|
|
89
|
+
/** Unavailable. */
|
|
90
|
+
disabled?: boolean;
|
|
91
|
+
/** The value is shown but cannot be changed. */
|
|
92
|
+
readOnly?: boolean;
|
|
93
|
+
/** Whether a value must be chosen before the form is submitted. */
|
|
94
|
+
required?: boolean;
|
|
95
|
+
/** Identifies the field when a form is submitted. */
|
|
96
|
+
name?: string;
|
|
97
|
+
/** The popup is open. Use with `onOpenChange` for a controlled popup. */
|
|
98
|
+
open?: boolean;
|
|
99
|
+
/** Whether the popup starts open. */
|
|
100
|
+
defaultOpen?: boolean;
|
|
101
|
+
onOpenChange?: (open: boolean) => void;
|
|
102
|
+
/** Accessible name of the clear button. */
|
|
103
|
+
clearLabel?: string;
|
|
104
|
+
/** Accessible name of a chip's remove button. Receives the chip's label. */
|
|
105
|
+
removeLabel?: (label: string) => string;
|
|
106
|
+
/** A ref to the text input the user types into. */
|
|
107
|
+
inputRef?: React.Ref<HTMLInputElement>;
|
|
108
|
+
id?: string;
|
|
109
|
+
className?: string;
|
|
110
|
+
style?: React.CSSProperties;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* A field you can type into and also choose from.
|
|
114
|
+
*
|
|
115
|
+
* The shell is a TextField's, wearing a chevron, exactly as Select's trigger is
|
|
116
|
+
* — the three have to be indistinguishable in a form or the form looks
|
|
117
|
+
* assembled rather than designed. What is different is what the text does: it
|
|
118
|
+
* filters the list, and — unless `allowCustom` is off — it can become the value
|
|
119
|
+
* itself, offered as the last row rather than committed silently on blur.
|
|
120
|
+
*
|
|
121
|
+
* With `multiple` the chosen values become [Chip](../../display/chip)s inside
|
|
122
|
+
* the field and the input goes on filtering after each one, so a set of tags is
|
|
123
|
+
* built without the field ever closing.
|
|
124
|
+
*
|
|
125
|
+
* Base UI owns everything hard about this: the filtering and its collator, the
|
|
126
|
+
* popup's positioning and flipping, the `combobox`/`listbox` wiring, arrow-key
|
|
127
|
+
* navigation across both the list and the chips, and the hidden input that
|
|
128
|
+
* makes the whole thing submit with a form.
|
|
129
|
+
*/
|
|
130
|
+
export declare function Combobox<Multiple extends boolean | undefined = false>({ variant, size, color, density, elevation, items, multiple, value, defaultValue, onValueChange, onInputValueChange, allowCustom, customLabel, clearable, emptyMessage, limit, placeholder, label, description, error, invalid, startIcon, fullWidth, disabled, readOnly, required, name, open, defaultOpen, onOpenChange, clearLabel, removeLabel, inputRef, id, className, style }: ComboboxProps<Multiple>): React.JSX.Element;
|
|
131
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as l from"react";import{Combobox as n}from"@base-ui/react/combobox";import{Field as t}from"@base-ui/react/field";import{Chip as i}from"../chip/Chip";import{CheckIcon as r,ChevronIcon as o,CloseIcon as s,PlusIcon as c}from"../../internal/icons";import{chipRemoveClasses as d,controlHeightClasses as u,controlTextLeadingClasses as m,disabledClasses as p,fieldReadOnlyClasses as h,fieldRestClasses as b,focusWithinRingClasses as f,gapClasses as g,hasContent as x,iconClasses as v,metaTextClasses as y,paddingXClasses as N,radiusClasses as w,stackGapClasses as C,surfaceClasses as j,surfaceSlots as L,transitionClasses as I}from"../../internal/styles";const S=["group relative flex w-full cursor-text items-center","[-webkit-tap-highlight-color:transparent]",I,"focus-within:[transition-duration:0ms]",f,v].join(" "),$={xs:"min-h-5.5 py-0",sm:"min-h-6.5 py-[2px]",md:"min-h-8 py-[3px]",lg:"min-h-10 py-[4px]",xl:"min-h-12 py-[4px]"},O=[j,"max-h-[min(20rem,var(--available-height))] overflow-y-auto overscroll-contain","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(" "),V=["relative flex cursor-pointer items-center gap-2 select-none","rounded-(--neba-radius-xs) py-1.5 pe-2 ps-7",I,"data-[highlighted]:bg-(--n-soft-hover)","data-[selected]:text-(--n-accent) data-[selected]:font-medium","data-[disabled]:cursor-not-allowed data-[disabled]:text-(--neba-disabled-fg)","[outline:none]"].join(" "),_=["inline-flex h-[1lh] shrink-0 cursor-pointer items-center justify-center","rounded-(--neba-radius-xs) text-(--neba-muted-fg)","[transition:color_var(--neba-duration)_var(--neba-ease)]","hover:text-(--n-accent)","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-1","disabled:cursor-not-allowed disabled:text-(--neba-disabled-fg)"].join(" ");function z(e){return null==e?[]:Array.isArray(e)?e.slice():[e]}export function Combobox({variant:f="outline",size:v="md",color:j="primary",density:I="default",elevation:A=0,items:R,multiple:k,value:T,defaultValue:q,onValueChange:E,onInputValueChange:F,allowCustom:M=!0,customLabel:P,clearable:B=!1,emptyMessage:D="No matches",limit:G,placeholder:H,label:W,description:J,error:K,invalid:Q,startIcon:U,fullWidth:X=!1,disabled:Y=!1,readOnly:Z=!1,required:ee=!1,name:ae,open:le,defaultOpen:ne,onOpenChange:te,clearLabel:ie="Clear",removeLabel:re=e=>`Remove ${e}`,inputRef:oe,id:se,className:ce,style:de}){const ue=x(K),me=Q??ue,pe=me?"danger":j,he=!0===k,be=l.useMemo(()=>R.map(e=>({value:e.value,label:e.label??String(e.value),disabled:e.disabled})),[R]),[fe,ge]=l.useState(()=>z(q)),xe=void 0===T?fe:z(T),[ve,ye]=l.useState(""),Ne=l.useCallback(e=>be.find(a=>a.value===e)??{value:e,label:String(e),custom:!0},[be]),we=ve.trim(),Ce=we.toLocaleLowerCase(),je=""===we||be.some(e=>e.label.toLocaleLowerCase()===Ce||String(e.value).toLocaleLowerCase()===Ce)||xe.some(e=>String(e).toLocaleLowerCase()===Ce),Le=!M||Z||Y||je?null:we,Ie=l.useMemo(()=>null===Le?be:[...be,{value:Le,label:Le,custom:!0}],[be,Le]),Se=he?xe.map(Ne):xe.length>0?Ne(xe[0]):null;const $e=N[I][v],Oe=[S,m[v],w[v],g[v],he?$[v]:u[v],`${$e} pe-1.5`,Y?p[f]:Z?h[f]:b[f]].join(" "),Ve=["min-w-0 flex-1 self-stretch bg-transparent [font:inherit] text-inherit","[outline:none]","placeholder:text-(--neba-muted-fg)","caret-(--n-accent) selection:bg-(--n-soft-press)","disabled:cursor-not-allowed"].join(" "),_e=e(n.Input,{ref:oe,placeholder:H,className:he?`${Ve} min-w-16 h-[1lh]`:Ve});return a(t.Root,{disabled:Y,invalid:me,className:["flex-col align-top",C[v],X?"flex w-full":"inline-flex",ce??""].filter(Boolean).join(" "),style:{...L(pe,A),...de},children:[W?e(t.Label,{className:[y[v],"font-medium",Y?"text-(--neba-disabled-fg)":"text-(--neba-fg)"].join(" "),children:W}):null,a(n.Root,{id:se,name:ae,items:Ie,multiple:he,value:Se,onValueChange:e=>{!function(e){void 0===T&&ge(e),E?.(he?e:e[0]??null)}((null===e?[]:Array.isArray(e)?e:[e]).map(e=>e.value))},onInputValueChange:e=>{ye(e),F?.(e)},open:le,defaultOpen:ne,onOpenChange:e=>te?.(e),autoHighlight:!0,itemToStringLabel:e=>e.label,itemToStringValue:e=>String(e.value),isItemEqualToValue:(e,a)=>e.value===a.value,limit:G,disabled:Y,readOnly:Z,required:ee,children:[a(n.InputGroup,{className:Oe,children:[U?e("span",{className:"flex h-[1lh] shrink-0 items-center text-(--neba-muted-fg)",children:U}):null,he?e(n.Chips,{className:"flex min-w-0 flex-1 flex-wrap items-center gap-1",children:e(n.Value,{children:t=>a(l.Fragment,{children:[t.map(a=>e(n.Chip,{render:e(i,{variant:"outline",size:v,color:pe,density:"compact",disabled:Y,endIcon:Z||Y?null:e(n.ChipRemove,{"aria-label":re(a.label),className:d,children:e(s,{})})}),children:a.label},String(a.value))),_e]})})}):_e,B&&!Z?e(n.Clear,{"aria-label":ie,className:_,children:e(s,{})}):null,e(n.Trigger,{"aria-label":"string"==typeof W?void 0:"Open",className:_,children:e(n.Icon,{className:["flex items-center","[transition:rotate_var(--neba-duration)_var(--neba-ease)]","data-[popup-open]:rotate-180"].join(" "),children:e(o,{})})})]}),e(n.Portal,{children:e(n.Positioner,{className:"neba-portal z-50 [outline:none]",sideOffset:6,children:a(n.Popup,{className:`${O} ${w[v]} ${m[v]}`,children:[e(n.Empty,{className:"px-2 py-1.5 text-(--neba-muted-fg) empty:hidden",children:D}),e(n.List,{children:t=>e(n.Item,{value:t,disabled:t.disabled,className:V,children:t.custom?a(l.Fragment,{children:[e("span",{className:"absolute start-1.5 flex size-4 items-center justify-center text-(--n-accent) [&_svg]:size-4",children:e(c,{})}),e("span",{className:"truncate",children:P?P(t.label):`Add “${t.label}”`})]}):a(l.Fragment,{children:[e(n.ItemIndicator,{className:"absolute start-1.5 flex size-4 items-center justify-center",children:e(r,{})}),e("span",{className:"truncate",children:t.label})]})},`${t.custom?"custom:":""}${String(t.value)}`)})]})})})]}),J?e(t.Description,{className:`${y[v]} text-(--neba-muted-fg)`,children:J}):null,ue?e(t.Error,{match:!0,className:`${y[v]} text-(--n-accent)`,children:K}):null]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Combobox}from"./Combobox";
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Dialog as BaseUIDialog } from '@base-ui/react/dialog';
|
|
3
|
+
import type { NebaStyleProps } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* A dialog takes `size`, `color` and `density` and stops there.
|
|
6
|
+
*
|
|
7
|
+
* There is no `variant`: the three weights answer "how much does this surface
|
|
8
|
+
* assert itself against the page around it", and a modal has already taken the
|
|
9
|
+
* page. There is no `elevation` either — see `popupClasses`.
|
|
10
|
+
*/
|
|
11
|
+
export interface DialogProps extends Pick<NebaStyleProps, 'size' | 'color' | 'density'> {
|
|
12
|
+
/** The dialog is shown. Use with `onOpenChange` for a controlled dialog. */
|
|
13
|
+
open?: boolean;
|
|
14
|
+
/** Whether the dialog starts open, for an uncontrolled one. */
|
|
15
|
+
defaultOpen?: boolean;
|
|
16
|
+
onOpenChange?: (open: boolean) => void;
|
|
17
|
+
/**
|
|
18
|
+
* The element that opens the dialog, wired up by Base UI. Optional — a
|
|
19
|
+
* controlled dialog opened from somewhere else in the app needs no trigger at
|
|
20
|
+
* all, and passing one here is only a convenience for the common case.
|
|
21
|
+
*/
|
|
22
|
+
trigger?: React.ReactElement;
|
|
23
|
+
/** The heading. Rendered as the `<h2>` that names the dialog. */
|
|
24
|
+
title?: React.ReactNode;
|
|
25
|
+
/** A line under the title, and the dialog's accessible description. */
|
|
26
|
+
description?: React.ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* The bottom row. Laid out end-aligned, so a pair of buttons needs no wrapper
|
|
29
|
+
* of its own — and `DialogClose` is what makes one of them dismiss.
|
|
30
|
+
*/
|
|
31
|
+
actions?: React.ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* Draws a hairline between the header, the body and the actions instead of
|
|
34
|
+
* separating them with space. Worth turning on the moment the body scrolls:
|
|
35
|
+
* the lines are what say the header stayed put.
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
dividers?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Shows the × in the corner.
|
|
41
|
+
*
|
|
42
|
+
* On by default, unlike most booleans in the library. A modal dialog takes the
|
|
43
|
+
* page away until it is answered, and the visible way out should not have to
|
|
44
|
+
* be remembered.
|
|
45
|
+
* @default true
|
|
46
|
+
*/
|
|
47
|
+
showClose?: boolean;
|
|
48
|
+
/** Accessible name of the × button. */
|
|
49
|
+
closeLabel?: string;
|
|
50
|
+
/**
|
|
51
|
+
* A hard cap on the sheet's width, overriding the one `size` implies. Numbers
|
|
52
|
+
* are pixels. For the dialog whose content decides its width — a wide table,
|
|
53
|
+
* a narrow confirmation — rather than for tuning the scale, which is `size`.
|
|
54
|
+
*/
|
|
55
|
+
width?: number | string;
|
|
56
|
+
/**
|
|
57
|
+
* The sheet takes the full width its `size` allows.
|
|
58
|
+
*
|
|
59
|
+
* On by default, which is the other way round from every other component.
|
|
60
|
+
* Elsewhere `fullWidth` means "fill the container"; a dialog's container is
|
|
61
|
+
* the viewport, and a dialog that shrank to fit two words would be a tooltip.
|
|
62
|
+
* @default true
|
|
63
|
+
*/
|
|
64
|
+
fullWidth?: boolean;
|
|
65
|
+
/** Fills the viewport edge to edge. For a mobile-sized screen, or an editor. */
|
|
66
|
+
fullScreen?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Whether the page behind is taken away. `'trap-focus'` keeps the page
|
|
69
|
+
* scrollable and clickable while still holding focus inside.
|
|
70
|
+
* @default true
|
|
71
|
+
*/
|
|
72
|
+
modal?: boolean | 'trap-focus';
|
|
73
|
+
/**
|
|
74
|
+
* Whether pressing Escape or clicking outside closes the dialog. Turn it off
|
|
75
|
+
* for the dialog that has to be answered — and then give it actions that
|
|
76
|
+
* answer it, because there will be no other way out.
|
|
77
|
+
* @default true
|
|
78
|
+
*/
|
|
79
|
+
dismissible?: boolean;
|
|
80
|
+
/** The body. */
|
|
81
|
+
children?: React.ReactNode;
|
|
82
|
+
className?: string;
|
|
83
|
+
style?: React.CSSProperties;
|
|
84
|
+
}
|
|
85
|
+
export type DialogCloseProps = React.ComponentPropsWithoutRef<typeof BaseUIDialog.Close>;
|
|
86
|
+
/**
|
|
87
|
+
* Closes the dialog it is inside.
|
|
88
|
+
*
|
|
89
|
+
* Exported because an uncontrolled dialog has no `setOpen` for its Cancel
|
|
90
|
+
* button to call, and the alternative — making every dialog controlled — is a
|
|
91
|
+
* piece of state per dialog that exists only to answer a button.
|
|
92
|
+
*
|
|
93
|
+
* `render` is Base UI's own escape hatch, so a real Neba button dismisses:
|
|
94
|
+
* `<DialogClose render={<Button variant="text">Cancel</Button>} />`.
|
|
95
|
+
*/
|
|
96
|
+
export declare const DialogClose: React.ForwardRefExoticComponent<Omit<import("@base-ui/react").AlertDialogCloseProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
97
|
+
/**
|
|
98
|
+
* A sheet that takes the page away until it is answered.
|
|
99
|
+
*
|
|
100
|
+
* The sections are props rather than compound sub-components, exactly as they
|
|
101
|
+
* are on Card: the arrangement of a dialog is fixed — heading, description,
|
|
102
|
+
* body, actions — and what a caller wants to decide is what goes in each slot.
|
|
103
|
+
* Base UI owns everything hard about it: the focus trap, the scroll lock, the
|
|
104
|
+
* `aria-labelledby` / `aria-describedby` wiring, restoring focus to the trigger,
|
|
105
|
+
* and the inert page behind.
|
|
106
|
+
*
|
|
107
|
+
* What is left here is the surface, the width ladder and the scroll behaviour —
|
|
108
|
+
* the header and the actions stay put while only the body scrolls, which is why
|
|
109
|
+
* `dividers` matters more here than on a Card.
|
|
110
|
+
*/
|
|
111
|
+
export declare function Dialog({ size, color, density, open, defaultOpen, onOpenChange, trigger, title, description, actions, dividers, showClose, closeLabel, width, fullWidth, fullScreen, modal, dismissible, className, style, children }: DialogProps): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import{Dialog as l}from"@base-ui/react/dialog";import{boxPaddingXClasses as n,boxPaddingYClasses as i}from"../box/Box";import{CloseIcon as o}from"../../internal/icons";import{hasContent as r,metaTextClasses as t,radiusClasses as s,sheetBodyClasses as d,sheetHeaderGapClasses as c,sheetSectionGapClasses as m,sheetTitleClasses as f,surfaceClasses as u,surfaceSlots as p}from"../../internal/styles";const x={xs:"max-w-80",sm:"max-w-96",md:"max-w-lg",lg:"max-w-2xl",xl:"max-w-4xl"},b=[u,"relative flex w-full flex-col overflow-hidden","border text-(--neba-fg) bg-(--n-panel-press)","[border-color:var(--n-line)]","[box-shadow:var(--neba-shadow-3),var(--neba-plate-glass)]","[outline:none]","[transition:opacity_var(--neba-duration)_var(--neba-ease)]","data-[starting-style]:opacity-0 data-[ending-style]:opacity-0"].join(" "),h=["fixed inset-0 z-50 bg-(--neba-scrim)","[backdrop-filter:blur(2px)]","[transition:opacity_var(--neba-duration)_var(--neba-ease)]","data-[starting-style]:opacity-0 data-[ending-style]:opacity-0"].join(" "),g="border-t [border-color:var(--n-line)]";export const DialogClose=l.Close;export function Dialog({size:u="md",color:v="primary",density:y="default",open:w,defaultOpen:j,onOpenChange:$,trigger:_,title:N,description:k,actions:C,dividers:z=!1,showClose:B=!0,closeLabel:D="Close",width:O,fullWidth:P=!0,fullScreen:T=!1,modal:W=!0,dismissible:L=!0,className:R,style:S,children:V}){const q=n[y][u],A=i[y][u],E=z?`${q} ${A}`:q,F=r(N)||r(k),G=r(C);return a(l.Root,{open:w,defaultOpen:j,modal:W,disablePointerDismissal:!L,onOpenChange:(e,a)=>{L||e||"escape-key"!==a.reason?$?.(e):a.cancel()},children:[_?e(l.Trigger,{render:_}):null,a(l.Portal,{children:[e(l.Backdrop,{className:`neba-portal ${h}`}),e(l.Viewport,{className:["neba-portal fixed inset-0 z-50 flex justify-center",T?"items-stretch":"items-center p-4"].join(" "),children:a(l.Popup,{className:[b,d[u],T?"h-full max-w-none rounded-none":`max-h-full ${s[u]} ${void 0===O?x[u]:""}`,T||P?"":"w-auto",z?"":`${A} ${m[u]}`,R??""].filter(Boolean).join(" "),style:{...p(v,3),...void 0===O?null:{maxWidth:"number"==typeof O?`${O}px`:O},...S},children:[F||B?a("div",{className:`flex shrink-0 items-start gap-3 ${E}`,children:[a("div",{className:`flex min-w-0 flex-1 flex-col ${c[u]}`,children:[r(N)?e(l.Title,{className:`font-semibold ${f[u]} m-0`,children:N}):null,r(k)?e(l.Description,{className:`m-0 text-(--neba-muted-fg) ${t[u]}`,children:k}):null]}),B?e(l.Close,{"aria-label":D,className:["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(" "),children:e(o,{})}):null]}):null,r(V)?e("div",{className:["min-h-0 flex-1 overflow-y-auto overscroll-contain",E,z&&(F||B)?g:""].filter(Boolean).join(" "),children:V}):null,G?e("div",{className:["flex shrink-0 flex-wrap items-center justify-end gap-2",E,z?g:""].filter(Boolean).join(" "),children:C}):null]})})]})]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Dialog,DialogClose}from"./Dialog";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaAlign, NebaColor, NebaOrientation, NebaSize } from '../../types';
|
|
3
|
+
/** Where the label sits along a labelled divider. Ignored without a label. */
|
|
4
|
+
export type DividerTextAlign = NebaAlign;
|
|
5
|
+
export interface DividerProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'children'> {
|
|
6
|
+
/**
|
|
7
|
+
* Which way the line runs. A vertical divider has no height of its own — it
|
|
8
|
+
* stretches to its flex parent, the way a rule between two toolbar groups
|
|
9
|
+
* should.
|
|
10
|
+
* @default 'horizontal'
|
|
11
|
+
*/
|
|
12
|
+
orientation?: NebaOrientation;
|
|
13
|
+
/**
|
|
14
|
+
* Semantic colour role. Only the hairline is coloured, and only faintly: this
|
|
15
|
+
* is the same `--n-line` a Card scores its sections with, so a standalone
|
|
16
|
+
* divider and a card's internal one are the same line.
|
|
17
|
+
* @default 'primary'
|
|
18
|
+
*/
|
|
19
|
+
color?: NebaColor;
|
|
20
|
+
/** Type scale of the label. Nothing else on a divider has a size. */
|
|
21
|
+
size?: NebaSize;
|
|
22
|
+
/** A label set into the line — "OR" between two sign-in options. */
|
|
23
|
+
children?: React.ReactNode;
|
|
24
|
+
/**
|
|
25
|
+
* Where the label sits. `center` splits the line in half; `start` and `end`
|
|
26
|
+
* leave a short stub on the near side so the label still reads as set *into*
|
|
27
|
+
* the rule rather than floating above it.
|
|
28
|
+
* @default 'center'
|
|
29
|
+
*/
|
|
30
|
+
textAlign?: DividerTextAlign;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A rule between two things.
|
|
34
|
+
*
|
|
35
|
+
* With no children it is Base UI's `Separator` and nothing else — a real
|
|
36
|
+
* `role="separator"` with the right `aria-orientation`. With children the line
|
|
37
|
+
* breaks around the label.
|
|
38
|
+
*
|
|
39
|
+
* `separator` is not a name-from-content role, so the visible label does *not*
|
|
40
|
+
* become the accessible name on its own: a screen reader would announce a bare
|
|
41
|
+
* "separator" and the word "OR" would be read as loose text somewhere nearby.
|
|
42
|
+
* A string label is therefore copied into `aria-label`. Anything richer is left
|
|
43
|
+
* alone — only the caller knows which part of it is the name.
|
|
44
|
+
*
|
|
45
|
+
* There is no `variant` and no `elevation`: a divider is not a surface. It has
|
|
46
|
+
* no acrylic, catches no light and casts no shadow.
|
|
47
|
+
*/
|
|
48
|
+
export declare const Divider: React.ForwardRefExoticComponent<DividerProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as t from"react";import{Separator as a}from"@base-ui/react/separator";import{metaTextClasses as l}from"../../internal/styles";const n="[border-color:var(--n-line)]",i={horizontal:{start:["w-4 shrink-0","flex-1"],center:["flex-1","flex-1"],end:["flex-1","w-4 shrink-0"]},vertical:{start:["h-4 shrink-0","flex-1"],center:["flex-1","flex-1"],end:["flex-1","h-4 shrink-0"]}},o={xs:"gap-1.5",sm:"gap-2",md:"gap-3",lg:"gap-3.5",xl:"gap-4"};export const Divider=t.forwardRef(function({orientation:t="horizontal",color:s="primary",size:c="md",textAlign:f="center",className:m,style:d,children:h,...p},x){const u="vertical"===t,w={"--n-line":`var(--neba-${s}-line)`};if(!(null!=h&&!1!==h&&""!==h))return e(a,{ref:x,orientation:t,className:[u?"w-0 self-stretch border-l":"h-0 w-full border-t",n,m??""].filter(Boolean).join(" "),style:{...w,...d},...p});const[g,b]=i[t][f],v=u?"w-0 border-l":"h-0 border-t";return r(a,{ref:x,orientation:t,"aria-label":"string"==typeof h?h:void 0,className:["flex items-center",u?"w-auto flex-col self-stretch":"w-full",o[c],m??""].filter(Boolean).join(" "),style:{...w,...d},...p,children:[e("span",{"aria-hidden":"true",className:`${v} ${g} ${n}`}),e("span",{className:["shrink-0 whitespace-nowrap text-(--neba-muted-fg)",l[c],u?"[writing-mode:vertical-rl]":""].filter(Boolean).join(" "),children:h}),e("span",{"aria-hidden":"true",className:`${v} ${b} ${n}`})]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Divider}from"./Divider";
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
/** Why a file was turned away. One reason per file, in the order they are checked. */
|
|
4
|
+
export type FileRejectionReason = 'type' | 'size' | 'count';
|
|
5
|
+
export interface FileRejection {
|
|
6
|
+
file: File;
|
|
7
|
+
reason: FileRejectionReason;
|
|
8
|
+
}
|
|
9
|
+
export interface FilePickerProps extends NebaStyleProps {
|
|
10
|
+
/**
|
|
11
|
+
* Drop shadow depth. `0` (the default) is flat.
|
|
12
|
+
* @default 0
|
|
13
|
+
*/
|
|
14
|
+
elevation?: NebaElevation;
|
|
15
|
+
/**
|
|
16
|
+
* Which files the browser's own picker offers, in the `accept` grammar —
|
|
17
|
+
* `'image/*,.pdf'`. Dropped files are checked against it too, which the
|
|
18
|
+
* attribute alone does not do.
|
|
19
|
+
*/
|
|
20
|
+
accept?: string;
|
|
21
|
+
/** Whether more than one file may be chosen. @default false */
|
|
22
|
+
multiple?: boolean;
|
|
23
|
+
/** The largest a single file may be, in bytes. */
|
|
24
|
+
maxSize?: number;
|
|
25
|
+
/**
|
|
26
|
+
* How many files may be held at once. Implies `multiple`, and is checked
|
|
27
|
+
* against what is already chosen rather than against one drop.
|
|
28
|
+
*/
|
|
29
|
+
maxFiles?: number;
|
|
30
|
+
/** The chosen files. Use with `onFilesChange` for a controlled picker. */
|
|
31
|
+
value?: readonly File[];
|
|
32
|
+
/** The initially chosen files, for an uncontrolled one. */
|
|
33
|
+
defaultValue?: readonly File[];
|
|
34
|
+
onFilesChange?: (files: File[]) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Called with everything that was turned away, and why. Without it a rejected
|
|
37
|
+
* file disappears silently, which is the single worst thing a dropzone does.
|
|
38
|
+
*/
|
|
39
|
+
onReject?: (rejections: FileRejection[]) => void;
|
|
40
|
+
/** Label above the box. */
|
|
41
|
+
label?: React.ReactNode;
|
|
42
|
+
/** Helper text below the box. */
|
|
43
|
+
description?: React.ReactNode;
|
|
44
|
+
/** Error message below. Its presence also turns the picker invalid. */
|
|
45
|
+
error?: React.ReactNode;
|
|
46
|
+
/** Forces the invalid state without a message. Defaults to `!!error`. */
|
|
47
|
+
invalid?: boolean;
|
|
48
|
+
/** The line inside the box. Defaults to an English sentence. */
|
|
49
|
+
title?: React.ReactNode;
|
|
50
|
+
/** The line under it — what is accepted, how big, how many. */
|
|
51
|
+
hint?: React.ReactNode;
|
|
52
|
+
/** The glyph above the title. Pass `null` for a box with no picture in it. */
|
|
53
|
+
icon?: React.ReactNode;
|
|
54
|
+
/**
|
|
55
|
+
* Lists the chosen files under the box, each with a way to remove it.
|
|
56
|
+
* @default true
|
|
57
|
+
*/
|
|
58
|
+
showList?: boolean;
|
|
59
|
+
/** Accessible name of a file's remove button. Receives the file's name. */
|
|
60
|
+
removeLabel?: (name: string) => string;
|
|
61
|
+
/** Stretches to the width of the container. @default true */
|
|
62
|
+
fullWidth?: boolean;
|
|
63
|
+
/** Unavailable. */
|
|
64
|
+
disabled?: boolean;
|
|
65
|
+
/** The files are shown but cannot be added to or removed. */
|
|
66
|
+
readOnly?: boolean;
|
|
67
|
+
/** Whether a file must be chosen before the form is submitted. */
|
|
68
|
+
required?: boolean;
|
|
69
|
+
/** Identifies the field when a form is submitted. */
|
|
70
|
+
name?: string;
|
|
71
|
+
id?: string;
|
|
72
|
+
className?: string;
|
|
73
|
+
style?: React.CSSProperties;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* `1.4 MB`, in the units a person reading a file list expects.
|
|
77
|
+
*
|
|
78
|
+
* Base 1000 rather than 1024, and `MB` rather than `MiB`: it is the number every
|
|
79
|
+
* operating system's file browser shows, and a picker that disagrees with the
|
|
80
|
+
* Finder about how big the file is has picked a fight it cannot win.
|
|
81
|
+
*/
|
|
82
|
+
export declare function formatFileSize(bytes: number): string;
|
|
83
|
+
/**
|
|
84
|
+
* A box you drop files on, or click to open the file dialog.
|
|
85
|
+
*
|
|
86
|
+
* There is no Base UI primitive under this, and the fallback the library allows
|
|
87
|
+
* — plain React and DOM — is the right one: a dropzone is a `<div>` listening
|
|
88
|
+
* for four drag events and an `<input type="file">` it clicks for you. There is
|
|
89
|
+
* no popup to position, no focus to trap and no roving anything.
|
|
90
|
+
*
|
|
91
|
+
* What that leaves is the part every hand-rolled dropzone gets wrong. The drag
|
|
92
|
+
* counter is one of them: `dragenter` and `dragleave` both fire as the pointer
|
|
93
|
+
* crosses a *child* of the zone, so a zone that toggles a boolean flickers the
|
|
94
|
+
* whole time a file is being dragged across its own contents. Counting them is
|
|
95
|
+
* the fix. The other is `accept`, which the browser enforces on its own dialog
|
|
96
|
+
* and never on a drop, so a dropzone that only sets the attribute accepts
|
|
97
|
+
* anything the moment a file arrives by drag.
|
|
98
|
+
*
|
|
99
|
+
* The shell is a `<div>` and the pressable area inside it is a real `<button>`,
|
|
100
|
+
* with the file list outside that button — the same shape Chip and ListItem
|
|
101
|
+
* use, because the remove buttons cannot be nested inside the browse button.
|
|
102
|
+
*/
|
|
103
|
+
export declare const FilePicker: React.ForwardRefExoticComponent<FilePickerProps & React.RefAttributes<HTMLInputElement>>;
|