neba 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -7
- package/dist/components/alert/Alert.d.ts +7 -1
- package/dist/components/alert/Alert.js +1 -1
- package/dist/components/animate-appear/AnimateAppear.d.ts +55 -0
- package/dist/components/animate-appear/AnimateAppear.js +1 -0
- package/dist/components/animate-appear/index.d.ts +2 -0
- package/dist/components/animate-appear/index.js +1 -0
- package/dist/components/animate-blink/AnimateBlink.d.ts +31 -0
- package/dist/components/animate-blink/AnimateBlink.js +1 -0
- package/dist/components/animate-blink/index.d.ts +2 -0
- package/dist/components/animate-blink/index.js +1 -0
- package/dist/components/animate-fade/AnimateFade.d.ts +31 -0
- package/dist/components/animate-fade/AnimateFade.js +1 -0
- package/dist/components/animate-fade/index.d.ts +2 -0
- package/dist/components/animate-fade/index.js +1 -0
- package/dist/components/animate-grow/AnimateGrow.d.ts +41 -0
- package/dist/components/animate-grow/AnimateGrow.js +1 -0
- package/dist/components/animate-grow/index.d.ts +2 -0
- package/dist/components/animate-grow/index.js +1 -0
- package/dist/components/animate-headline/AnimateHeadline.d.ts +55 -0
- package/dist/components/animate-headline/AnimateHeadline.js +1 -0
- package/dist/components/animate-headline/index.d.ts +2 -0
- package/dist/components/animate-headline/index.js +1 -0
- package/dist/components/animate-lighting/AnimateLighting.d.ts +62 -0
- package/dist/components/animate-lighting/AnimateLighting.js +1 -0
- package/dist/components/animate-lighting/index.d.ts +2 -0
- package/dist/components/animate-lighting/index.js +1 -0
- package/dist/components/animate-marquee/AnimateMarquee.d.ts +65 -0
- package/dist/components/animate-marquee/AnimateMarquee.js +1 -0
- package/dist/components/animate-marquee/index.d.ts +2 -0
- package/dist/components/animate-marquee/index.js +1 -0
- package/dist/components/animate-rotate/AnimateRotate.d.ts +48 -0
- package/dist/components/animate-rotate/AnimateRotate.js +1 -0
- package/dist/components/animate-rotate/index.d.ts +2 -0
- package/dist/components/animate-rotate/index.js +1 -0
- package/dist/components/animate-slide/AnimateSlide.d.ts +46 -0
- package/dist/components/animate-slide/AnimateSlide.js +1 -0
- package/dist/components/animate-slide/index.d.ts +2 -0
- package/dist/components/animate-slide/index.js +1 -0
- package/dist/components/animate-typing/AnimateTyping.d.ts +58 -0
- package/dist/components/animate-typing/AnimateTyping.js +1 -0
- package/dist/components/animate-typing/index.d.ts +2 -0
- package/dist/components/animate-typing/index.js +1 -0
- package/dist/components/animate-zoom/AnimateZoom.d.ts +37 -0
- package/dist/components/animate-zoom/AnimateZoom.js +1 -0
- package/dist/components/animate-zoom/index.d.ts +2 -0
- package/dist/components/animate-zoom/index.js +1 -0
- package/dist/components/aspect-ratio/AspectRatio.d.ts +63 -0
- package/dist/components/aspect-ratio/AspectRatio.js +1 -0
- package/dist/components/aspect-ratio/index.d.ts +2 -0
- package/dist/components/aspect-ratio/index.js +1 -0
- package/dist/components/avatar/Avatar.d.ts +106 -0
- package/dist/components/avatar/Avatar.js +1 -0
- package/dist/components/avatar/index.d.ts +2 -0
- package/dist/components/avatar/index.js +1 -0
- package/dist/components/blockquote/Blockquote.d.ts +7 -1
- package/dist/components/blockquote/Blockquote.js +1 -1
- package/dist/components/box/Box.d.ts +7 -1
- package/dist/components/box/Box.js +1 -1
- package/dist/components/breadcrumb/Breadcrumb.d.ts +92 -0
- package/dist/components/breadcrumb/Breadcrumb.js +1 -0
- package/dist/components/breadcrumb/index.d.ts +2 -0
- package/dist/components/breadcrumb/index.js +1 -0
- package/dist/components/chat-bubble/ChatBubble.d.ts +114 -0
- package/dist/components/chat-bubble/ChatBubble.js +1 -0
- package/dist/components/chat-bubble/index.d.ts +2 -0
- package/dist/components/chat-bubble/index.js +1 -0
- package/dist/components/chip/Chip.d.ts +7 -1
- package/dist/components/chip/Chip.js +1 -1
- package/dist/components/color-picker/ColorPicker.d.ts +102 -0
- package/dist/components/color-picker/ColorPicker.js +1 -0
- package/dist/components/color-picker/index.d.ts +2 -0
- package/dist/components/color-picker/index.js +1 -0
- package/dist/components/drawer/Drawer.d.ts +145 -0
- package/dist/components/drawer/Drawer.js +1 -0
- package/dist/components/drawer/index.d.ts +2 -0
- package/dist/components/drawer/index.js +1 -0
- package/dist/components/icon/Icon.d.ts +8 -1
- package/dist/components/icon/Icon.js +1 -1
- package/dist/components/otp-field/OtpField.d.ts +85 -0
- package/dist/components/otp-field/OtpField.js +1 -0
- package/dist/components/otp-field/index.d.ts +2 -0
- package/dist/components/otp-field/index.js +1 -0
- package/dist/components/panes/Panes.d.ts +78 -0
- package/dist/components/panes/Panes.js +1 -0
- package/dist/components/panes/index.d.ts +2 -0
- package/dist/components/panes/index.js +1 -0
- package/dist/components/popover/Popover.d.ts +109 -0
- package/dist/components/popover/Popover.js +1 -0
- package/dist/components/popover/index.d.ts +2 -0
- package/dist/components/popover/index.js +1 -0
- package/dist/components/skeleton/Skeleton.d.ts +76 -0
- package/dist/components/skeleton/Skeleton.js +1 -0
- package/dist/components/skeleton/index.d.ts +2 -0
- package/dist/components/skeleton/index.js +1 -0
- package/dist/components/spoiler/Spoiler.d.ts +95 -0
- package/dist/components/spoiler/Spoiler.js +1 -0
- package/dist/components/spoiler/index.d.ts +2 -0
- package/dist/components/spoiler/index.js +1 -0
- package/dist/components/text-link/TextLink.d.ts +82 -0
- package/dist/components/text-link/TextLink.js +1 -0
- package/dist/components/text-link/index.d.ts +2 -0
- package/dist/components/text-link/index.js +1 -0
- package/dist/components/tree-view/TreeView.d.ts +115 -0
- package/dist/components/tree-view/TreeView.js +1 -0
- package/dist/components/tree-view/index.d.ts +2 -0
- package/dist/components/tree-view/index.js +1 -0
- package/dist/components/typography/Typography.d.ts +7 -1
- package/dist/components/typography/Typography.js +1 -1
- package/dist/index.d.ts +24 -0
- package/dist/index.js +1 -1
- package/dist/internal/animate.d.ts +175 -0
- package/dist/internal/animate.js +1 -0
- package/dist/internal/color.d.ts +108 -0
- package/dist/internal/color.js +1 -0
- package/dist/internal/i18n.d.ts +103 -0
- package/dist/internal/i18n.js +1 -0
- package/dist/internal/icons.d.ts +23 -0
- package/dist/internal/icons.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/tailwind.css +504 -0
- package/dist/types.d.ts +112 -0
- package/package.json +14 -14
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaColor, NebaOrientation, NebaSize } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* A pane's share of the split, as a percentage of the container or as a CSS
|
|
5
|
+
* length.
|
|
6
|
+
*
|
|
7
|
+
* A bare number is a percentage — that is what a split is usually described in,
|
|
8
|
+
* and a percentage keeps its meaning when the window changes size. A string is
|
|
9
|
+
* an absolute length (`'240px'`, `'15rem'`, `'20%'`), which is what a sidebar
|
|
10
|
+
* with a minimum actually needs: "at least 200 pixels" does not survive being
|
|
11
|
+
* written down as a percentage of a width nobody knows yet.
|
|
12
|
+
*/
|
|
13
|
+
export type PaneSize = number | string;
|
|
14
|
+
export interface PanesProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
|
15
|
+
/**
|
|
16
|
+
* Which way the panes run. `horizontal` puts them side by side with upright
|
|
17
|
+
* handles between them; `vertical` stacks them.
|
|
18
|
+
* @default 'horizontal'
|
|
19
|
+
*/
|
|
20
|
+
orientation?: NebaOrientation;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the handles between the panes can be dragged. Turn it off for a
|
|
23
|
+
* split that is a layout rather than a control.
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
resizable?: boolean;
|
|
27
|
+
/** The colour family the handles light up in. @default 'primary' */
|
|
28
|
+
color?: NebaColor;
|
|
29
|
+
/** How thick a handle is, and how far it reaches. @default 'md' */
|
|
30
|
+
size?: NebaSize;
|
|
31
|
+
/** Fires with every pane's share, in percent, while a handle is dragged. */
|
|
32
|
+
onResize?: (sizes: number[]) => void;
|
|
33
|
+
/** Fires once, with the same shape, when the handle is let go. */
|
|
34
|
+
onResizeEnd?: (sizes: number[]) => void;
|
|
35
|
+
/** The Panes. Anything that is not a Pane is still laid out, but has no size. */
|
|
36
|
+
children?: React.ReactNode;
|
|
37
|
+
}
|
|
38
|
+
export interface PaneProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
|
39
|
+
/**
|
|
40
|
+
* The share this pane starts with. Panes with no `defaultSize` split whatever
|
|
41
|
+
* is left over equally.
|
|
42
|
+
*/
|
|
43
|
+
defaultSize?: PaneSize;
|
|
44
|
+
/** How small it may be dragged. @default 0 */
|
|
45
|
+
minSize?: PaneSize;
|
|
46
|
+
/** How large it may be dragged. Unbounded when left out. */
|
|
47
|
+
maxSize?: PaneSize;
|
|
48
|
+
/** What is inside the pane. */
|
|
49
|
+
children?: React.ReactNode;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A set of panes with draggable handles between them.
|
|
53
|
+
*
|
|
54
|
+
* The panes are sized in **fractions**, written out as
|
|
55
|
+
* `flex-basis: calc((100% - gutters) * fraction)`. That is the one decision the
|
|
56
|
+
* rest of this file follows from: a split described in percentages survives the
|
|
57
|
+
* window being resized without a single line of JavaScript running, so the
|
|
58
|
+
* component measures itself only twice — once on mount, to turn a `'240px'`
|
|
59
|
+
* default into a fraction, and once at the start of each drag, to know what a
|
|
60
|
+
* pixel of pointer movement is worth.
|
|
61
|
+
*
|
|
62
|
+
* The handles are interleaved here rather than written by the caller, so the
|
|
63
|
+
* children of a Panes are just panes. That does mean the direct children have to
|
|
64
|
+
* *be* Panes: the constraints are read off their props, and a Pane wrapped in
|
|
65
|
+
* something else is a pane with no minimum.
|
|
66
|
+
*/
|
|
67
|
+
export declare const Panes: React.ForwardRefExoticComponent<PanesProps & React.RefAttributes<HTMLDivElement>>;
|
|
68
|
+
/**
|
|
69
|
+
* One region of a split.
|
|
70
|
+
*
|
|
71
|
+
* It carries no surface of its own on purpose: a Panes is layout, and the moment
|
|
72
|
+
* a pane drew a sheet it would stop being usable as the thing a Card, a Table or
|
|
73
|
+
* an editor is put inside. Put a Box or a Card in it when a surface is wanted.
|
|
74
|
+
*
|
|
75
|
+
* `defaultSize`, `minSize` and `maxSize` are read by the Panes around it rather
|
|
76
|
+
* than used here — a pane cannot know what "half" is, only the split can.
|
|
77
|
+
*/
|
|
78
|
+
export declare const Pane: React.ForwardRefExoticComponent<PaneProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as n from"react";import{cx as r,transitionClasses as o}from"../../internal/styles";const i=n.createContext({basis:null}),a={xs:"basis-1",sm:"basis-1.5",md:"basis-2",lg:"basis-2.5",xl:"basis-3"},s={xs:4,sm:6,md:8,lg:10,xl:12};function l(e,t,n){if(null==e)return;if("number"==typeof e)return t*e/100;const r=/^\s*(-?[\d.]+)\s*(px|rem|em|%)\s*$/.exec(e);if(!r)return;const o=Number(r[1]);if(!Number.isNaN(o))switch(r[2]){case"px":return o;case"%":return t*o/100;case"rem":return o*parseFloat(getComputedStyle(document.documentElement).fontSize||"16");case"em":return o*parseFloat(n&&getComputedStyle(n).fontSize||"16");default:return}}export const Panes=n.forwardRef(function({orientation:c="horizontal",resizable:u=!0,color:d="primary",size:m="md",onResize:f,onResizeEnd:p,className:h,style:g,children:v,...x},b){const y=n.Children.toArray(v).filter(n.isValidElement),z=y.length,w=n.useRef(null),S=n.useCallback(e=>{w.current=e,"function"==typeof b?b(e):b&&(b.current=e)},[b]),R=n.useRef([]);R.current=y.map(e=>e.props);const[C,E]=n.useState(null),N=C&&C.length===z?C:null,M=n.useRef(null);M.current=N;const L="horizontal"===c,$=s[m]*Math.max(0,z-1);function k(e){const t=w.current,n=M.current;if(!u||!t||!n||void 0===n[e+1])return null;const r=t.getBoundingClientRect(),o=(L?r.width:r.height)-$;if(o<=0)return null;const i=R.current[e],a=R.current[e+1],s=n[e]*o,c=s+n[e+1]*o,d=Math.max(l(i?.minSize,o,t)??0,c-(l(a?.maxSize,o,t)??c)),m=Math.min(l(i?.maxSize,o,t)??c,c-(l(a?.minSize,o,t)??0));return m<d?null:{root:t,current:n,extent:o,start:s,pair:c,resize(t){const r=Math.min(m,Math.max(d,s+t)),i=[...n];return i[e]=r/o,i[e+1]=(c-r)/o,E(i),f?.(i.map(e=>100*e)),i}}}n.useEffect(()=>{const e=w.current;if(!e)return;const t=()=>{const t=e.getBoundingClientRect(),n=(L?t.width:t.height)-$;n<=0||E(t=>t&&t.length===z?t:function(e,t,n){const r=e.map(e=>l(e.defaultSize,t,n)),o=r.reduce((e,t)=>e+(t??0),0),i=r.filter(e=>void 0===e).length,a=i>0?Math.max(0,t-o)/i:0,s=r.map(e=>Math.max(0,e??a)),c=s.reduce((e,t)=>e+t,0);return c<=0?s.map(()=>1/Math.max(1,s.length)):s.map(e=>e/c)}(R.current,n,e))};t();const n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[z,L,$]);const A=r("group/handle relative z-1 flex shrink-0 grow-0 items-center justify-center",a[m],o,"[outline:none] focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-0",u?r(L?"cursor-col-resize":"cursor-row-resize","hover:bg-(--n-soft) data-[dragging]:bg-(--n-soft)"):"");return e("div",{ref:S,className:r("flex h-full w-full",L?"flex-row":"flex-col","min-h-0 min-w-0",h),style:{"--n-accent":`var(--neba-${d}-accent)`,"--n-soft":`var(--neba-${d}-soft)`,"--n-ring":`var(--neba-${d}-ring)`,...g},...x,children:y.map((a,s)=>t(n.Fragment,{children:[s>0?e("div",{role:"separator","aria-orientation":L?"vertical":"horizontal","aria-valuenow":N?Math.round(100*N[s-1]):void 0,"aria-valuemin":0,"aria-valuemax":100,"aria-disabled":!u||void 0,tabIndex:u?0:-1,className:A,onPointerDown:e=>{0===e.button&&function(e,t){const n=k(e);if(!n)return;const r=t.currentTarget;r.setPointerCapture(t.pointerId),r.dataset.dragging="true";const o=document.body.style.userSelect;document.body.style.userSelect="none";const i=L?t.clientX:t.clientY,a=L&&"rtl"===getComputedStyle(n.root).direction?-1:1;let s=n.current;const l=e=>{const t=L?e.clientX:e.clientY;s=n.resize((t-i)*a)},c=()=>{r.removeEventListener("pointermove",l),r.removeEventListener("pointerup",c),r.removeEventListener("pointercancel",c),delete r.dataset.dragging,document.body.style.userSelect=o,p?.(s.map(e=>100*e))};r.addEventListener("pointermove",l),r.addEventListener("pointerup",c),r.addEventListener("pointercancel",c)}(s-1,e)},onKeyDown:e=>{const t=L?"ArrowLeft":"ArrowUp",n=L?"ArrowRight":"ArrowDown";e.key!==t&&e.key!==n||(e.preventDefault(),function(e,t){const n=k(e);if(!n)return;const r=n.resize(t);p?.(r.map(e=>100*e))}(s-1,e.key===n?16:-16))},children:e("span",{"aria-hidden":"true",className:r("pointer-events-none bg-(--neba-border)",o,L?"h-full w-px":"h-px w-full",u?"group-hover/handle:bg-(--n-accent) group-focus-visible/handle:bg-(--n-accent) group-data-[dragging]/handle:bg-(--n-accent)":"")})}):null,e(i.Provider,{value:{basis:N?`calc((100% - ${$}px) * ${N[s].toFixed(6)})`:null},children:a})]},a.key??s))})});export const Pane=n.forwardRef(function({defaultSize:t,minSize:o,maxSize:a,className:s,style:l,children:c,...u},d){const{basis:m}=n.useContext(i);return e("div",{ref:d,className:r("relative min-h-0 min-w-0 overflow-auto",s),style:{flex:m?`0 0 ${m}`:"1 1 0%",...l},...u,children:c})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Pane,Panes}from"./Panes";
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Popover as BaseUIPopover } from '@base-ui/react/popover';
|
|
3
|
+
import type { NebaAlign, NebaSide, NebaStyleProps } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* A popover 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", and a popup that had to be asked for has
|
|
9
|
+
* already been asserted. There is no `elevation` either: a popover floats over
|
|
10
|
+
* the page by definition, so it carries a shadow at level 3 and a prop that
|
|
11
|
+
* could sit it flat would be a prop that could stop it being a popover.
|
|
12
|
+
*/
|
|
13
|
+
export interface PopoverProps extends Pick<NebaStyleProps, 'size' | 'color' | 'density'> {
|
|
14
|
+
/**
|
|
15
|
+
* The element the popup hangs off and that opens it. Exactly one element,
|
|
16
|
+
* which must accept a ref and spread props — every Neba component does.
|
|
17
|
+
*
|
|
18
|
+
* Optional: a controlled popover anchored from elsewhere needs none, though it
|
|
19
|
+
* then has nothing to position against and will sit against the viewport.
|
|
20
|
+
*/
|
|
21
|
+
trigger?: React.ReactElement;
|
|
22
|
+
/** The heading, rendered as the element that names the popup. */
|
|
23
|
+
title?: React.ReactNode;
|
|
24
|
+
/** A line under the title, and the popup's accessible description. */
|
|
25
|
+
description?: React.ReactNode;
|
|
26
|
+
/** The body. */
|
|
27
|
+
children?: React.ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* Which edge of the trigger it appears on. Flips to the opposite side when
|
|
30
|
+
* there is no room, which is Base UI's doing and is the right behaviour.
|
|
31
|
+
* @default 'bottom'
|
|
32
|
+
*/
|
|
33
|
+
side?: NebaSide;
|
|
34
|
+
/** Where it sits along that edge. @default 'center' */
|
|
35
|
+
align?: NebaAlign;
|
|
36
|
+
/** Distance from the trigger, in pixels. @default 6 */
|
|
37
|
+
sideOffset?: number;
|
|
38
|
+
/** Shift along that edge, in pixels. @default 0 */
|
|
39
|
+
alignOffset?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Draws the little wedge pointing at the trigger.
|
|
42
|
+
*
|
|
43
|
+
* Off by default, unlike on [Tooltip](../feedback/tooltip). A tooltip is a
|
|
44
|
+
* filled plate and its wedge is the same solid colour; this surface is
|
|
45
|
+
* translucent over a blurred backdrop, and a wedge sticking out past the
|
|
46
|
+
* popup's own box cannot carry that backdrop with it. Turn it on where the
|
|
47
|
+
* trigger is far enough away that the popup needs to say what it belongs to.
|
|
48
|
+
* @default false
|
|
49
|
+
*/
|
|
50
|
+
arrow?: boolean;
|
|
51
|
+
/** Whether the popover is open. Use with `onOpenChange` for a controlled one. */
|
|
52
|
+
open?: boolean;
|
|
53
|
+
/** Whether it starts open, for an uncontrolled one. */
|
|
54
|
+
defaultOpen?: boolean;
|
|
55
|
+
onOpenChange?: (open: boolean) => void;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the page behind is taken away. `false` — the default — leaves the
|
|
58
|
+
* page scrollable and clickable, which is what separates a popover from a
|
|
59
|
+
* [Dialog](../feedback/dialog): it is a detail beside the page, not instead of
|
|
60
|
+
* it. `'trap-focus'` holds focus inside without locking the scroll.
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
modal?: boolean | 'trap-focus';
|
|
64
|
+
/**
|
|
65
|
+
* Whether pressing Escape or clicking outside closes the popup. Turn it off
|
|
66
|
+
* only for a popup with its own way out, because there will be no other.
|
|
67
|
+
* @default true
|
|
68
|
+
*/
|
|
69
|
+
dismissible?: boolean;
|
|
70
|
+
/** Shows the × in the corner. @default false */
|
|
71
|
+
showClose?: boolean;
|
|
72
|
+
/** Accessible name of the × button. */
|
|
73
|
+
closeLabel?: string;
|
|
74
|
+
/**
|
|
75
|
+
* A hard cap on the popup's width, overriding the one `size` implies. Numbers
|
|
76
|
+
* are pixels. For the popover whose content decides its width — a form, a
|
|
77
|
+
* single line of help — rather than for tuning the scale, which is `size`.
|
|
78
|
+
*/
|
|
79
|
+
width?: number | string;
|
|
80
|
+
className?: string;
|
|
81
|
+
style?: React.CSSProperties;
|
|
82
|
+
}
|
|
83
|
+
export type PopoverCloseProps = React.ComponentPropsWithoutRef<typeof BaseUIPopover.Close>;
|
|
84
|
+
/**
|
|
85
|
+
* Closes the popover it is inside.
|
|
86
|
+
*
|
|
87
|
+
* Exported for the reason `DialogClose` is: an uncontrolled popover has no
|
|
88
|
+
* `setOpen` for its Cancel button to call, and making every popover controlled
|
|
89
|
+
* is a piece of state per popover that exists only to answer a button.
|
|
90
|
+
*
|
|
91
|
+
* `render` is Base UI's own escape hatch, so a real Neba button dismisses:
|
|
92
|
+
* `<PopoverClose render={<Button variant="text">Cancel</Button>} />`.
|
|
93
|
+
*/
|
|
94
|
+
export declare const PopoverClose: React.ForwardRefExoticComponent<Omit<import("@base-ui/react").PopoverCloseProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
95
|
+
/**
|
|
96
|
+
* A sheet that opens beside the thing that opened it.
|
|
97
|
+
*
|
|
98
|
+
* The difference from a [Tooltip](../feedback/tooltip) is that this one can be
|
|
99
|
+
* reached: it stays up until it is dismissed, it can be entered with the
|
|
100
|
+
* pointer or the keyboard, and what is inside it can be clicked and typed into.
|
|
101
|
+
* The difference from a [Dialog](../feedback/dialog) is that it does not take
|
|
102
|
+
* the page — it is anchored to a control, and the page behind goes on working.
|
|
103
|
+
*
|
|
104
|
+
* Base UI owns everything hard about it: the anchoring and the flip at the
|
|
105
|
+
* window edge, the click-outside and Escape, returning focus to the trigger, and
|
|
106
|
+
* the `aria-labelledby` / `aria-describedby` wiring. What is left here is the
|
|
107
|
+
* surface, the width ladder and the header.
|
|
108
|
+
*/
|
|
109
|
+
export declare function Popover({ size, color, density, trigger, title, description, children, side, align, sideOffset, alignOffset, arrow, open, defaultOpen, onOpenChange, modal, dismissible, showClose, closeLabel, width, className, style }: PopoverProps): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import{Popover as o}from"@base-ui/react/popover";import{boxPaddingXClasses as t,boxPaddingYClasses as n}from"../box/Box";import{CloseIcon as s}from"../../internal/icons";import{hasContent as i,metaTextClasses as l,radiusClasses as r,sheetBodyClasses as d,sheetHeaderGapClasses as c,sheetSectionGapClasses as m,sheetTitleClasses as p,surfaceClasses as f,surfaceSlots as u}from"../../internal/styles";const h={xs:"max-w-56",sm:"max-w-64",md:"max-w-80",lg:"max-w-96",xl:"max-w-lg"},x=[f,"relative flex flex-col","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(" "),b={xs:8,sm:9,md:10,lg:11,xl:12};export const PopoverClose=o.Close;export function Popover({size:f="md",color:g="primary",density:v="default",trigger:w,title:y,description:N,children:_,side:k="bottom",align:j="center",sideOffset:O=6,alignOffset:C=0,arrow:P=!1,open:z,defaultOpen:$,onOpenChange:B,modal:L=!1,dismissible:M=!0,showClose:T=!1,closeLabel:W="Close",width:A,className:D,style:E}){const R=t[v][f],q=n[v][f],F=b[f],G=i(y)||i(N);return a(o.Root,{open:z,defaultOpen:$,modal:L,onOpenChange:(e,a)=>{M||e||"escape-key"!==a.reason&&"outside-press"!==a.reason&&"focus-out"!==a.reason?B?.(e):a.cancel()},children:[w?e(o.Trigger,{render:w}):null,e(o.Portal,{children:e(o.Positioner,{className:"neba-portal z-50 [outline:none]",side:k,align:j,sideOffset:O,alignOffset:C,children:a(o.Popup,{className:[x,r[f],d[f],m[f],R,q,void 0===A?h[f]:"",D??""].filter(Boolean).join(" "),style:{...u(g,3),...void 0===A?null:{maxWidth:"number"==typeof A?`${A}px`:A},...E},children:[P?e(o.Arrow,{className:["data-[side=top]:bottom-[-1px]","data-[side=bottom]:top-[-1px] data-[side=bottom]:rotate-180","data-[side=left]:right-[-1px] data-[side=left]:-rotate-90","data-[side=right]:left-[-1px] data-[side=right]:rotate-90"].join(" "),children:a("svg",{width:F,height:F/2,viewBox:"0 0 10 5","aria-hidden":"true",className:"block",children:[e("path",{d:"M0 0h10L5 5z",fill:"var(--n-panel-press)"}),e("path",{d:"M0 0 5 5 10 0",fill:"none",stroke:"var(--n-line)",strokeWidth:"1",vectorEffect:"non-scaling-stroke"})]})}):null,G||T?a("div",{className:"flex shrink-0 items-start gap-3",children:[a("div",{className:`flex min-w-0 flex-1 flex-col ${c[f]}`,children:[i(y)?e(o.Title,{className:`m-0 font-semibold ${p[f]}`,children:y}):null,i(N)?e(o.Description,{className:`m-0 text-(--neba-muted-fg) ${l[f]}`,children:N}):null]}),T?e(o.Close,{"aria-label":W,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(s,{})}):null]}):null,i(_)?e("div",{className:"min-w-0",children:_}):null]})})})]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Popover,PopoverClose}from"./Popover";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
+
import type { NebaColor, NebaSize } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* What the placeholder is standing in for.
|
|
6
|
+
*
|
|
7
|
+
* - `line` — a run of text. Sized off the type scale, so a `md` line is exactly
|
|
8
|
+
* as tall as the `md` type it will be replaced by.
|
|
9
|
+
* - `rect` — a block: an image, a chart, a card, a map.
|
|
10
|
+
* - `circle` — an avatar, or anything else round.
|
|
11
|
+
*/
|
|
12
|
+
export type NebaSkeletonShape = 'line' | 'rect' | 'circle';
|
|
13
|
+
export interface SkeletonProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
|
14
|
+
/** @default 'line' */
|
|
15
|
+
shape?: NebaSkeletonShape;
|
|
16
|
+
/**
|
|
17
|
+
* How many lines to draw, for `shape="line"`. The last one is drawn short, the
|
|
18
|
+
* way the last line of a paragraph is, so a block of them reads as prose
|
|
19
|
+
* rather than as a barcode. Ignored by the other two shapes.
|
|
20
|
+
* @default 1
|
|
21
|
+
*/
|
|
22
|
+
lines?: number;
|
|
23
|
+
/**
|
|
24
|
+
* The scale of the thing being stood in for: the type scale for a `line`, the
|
|
25
|
+
* diameter for a `circle`, the default block height for a `rect`.
|
|
26
|
+
* @default 'md'
|
|
27
|
+
*/
|
|
28
|
+
size?: NebaSize;
|
|
29
|
+
/**
|
|
30
|
+
* Colour family. `secondary` by default, and it is worth leaving there: a
|
|
31
|
+
* placeholder that carries a semantic colour is saying something about content
|
|
32
|
+
* that has not arrived yet.
|
|
33
|
+
* @default 'secondary'
|
|
34
|
+
*/
|
|
35
|
+
color?: NebaColor;
|
|
36
|
+
/** An explicit width. Numbers are pixels. */
|
|
37
|
+
width?: number | string;
|
|
38
|
+
/** An explicit height. Numbers are pixels. */
|
|
39
|
+
height?: number | string;
|
|
40
|
+
/**
|
|
41
|
+
* The travelling highlight. Turn it off for a page holding dozens of them, or
|
|
42
|
+
* where the wait is expected to be long enough that motion becomes noise.
|
|
43
|
+
*
|
|
44
|
+
* A reduced-motion preference already replaces the sweep with a colour pulse
|
|
45
|
+
* without being asked, so this is not the accessibility switch.
|
|
46
|
+
* @default true
|
|
47
|
+
*/
|
|
48
|
+
animated?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* What a screen reader is told, if anything.
|
|
51
|
+
*
|
|
52
|
+
* Unset — the default — the placeholder is `aria-hidden`, because a dozen
|
|
53
|
+
* boxes each announcing themselves is worse than silence. Give the *one*
|
|
54
|
+
* skeleton that stands for the whole region a label and it becomes a live
|
|
55
|
+
* `status` instead.
|
|
56
|
+
*/
|
|
57
|
+
label?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Renders something other than a `<div>`. Base UI's own escape hatch.
|
|
60
|
+
*/
|
|
61
|
+
render?: useRender.RenderProp;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The shape of something that has not loaded yet.
|
|
65
|
+
*
|
|
66
|
+
* It reserves the space the real thing will take, which is the whole job: a card
|
|
67
|
+
* that grows by 200px when its image arrives has moved everything below it while
|
|
68
|
+
* somebody was reading. A spinner cannot do that, which is why this is not a
|
|
69
|
+
* [ProgressCircular](../feedback/progress-circular) in a box.
|
|
70
|
+
*
|
|
71
|
+
* The three shapes are the three things a layout is made of — a run of text, a
|
|
72
|
+
* block and a circle — and each is sized off the ladder the real component uses,
|
|
73
|
+
* so a `md` line is as tall as `md` type and a `md` circle is exactly an
|
|
74
|
+
* [Avatar](../display/avatar) at `md`.
|
|
75
|
+
*/
|
|
76
|
+
export declare const Skeleton: React.ForwardRefExoticComponent<SkeletonProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import*as r from"react";import{useRender as l}from"@base-ui/react/use-render";import{controlHeightClasses as n,controlSquareClasses as d,surfaceSlots as o}from"../../internal/styles";const a={xs:"h-[0.6875rem]",sm:"h-[0.75rem]",md:"h-[0.8125rem]",lg:"h-[0.9375rem]",xl:"h-[1.0625rem]"},s={xs:"gap-[0.3125rem]",sm:"gap-[0.375rem]",md:"gap-[0.5625rem]",lg:"gap-[0.5625rem]",xl:"gap-[0.6875rem]"},m={xs:"rounded-[0.3125rem]",sm:"rounded-[0.3125rem]",md:"rounded-[0.375rem]",lg:"rounded-[0.4375rem]",xl:"rounded-[0.5rem]"},i={xs:"h-12",sm:"h-16",md:"h-20",lg:"h-28",xl:"h-36"},t={xs:"rounded-(--neba-radius-xs)",sm:"rounded-(--neba-radius-sm)",md:"rounded-(--neba-radius-md)",lg:"rounded-(--neba-radius-lg)",xl:"rounded-(--neba-radius-xl)"},u="relative overflow-hidden bg-(--n-soft-hover)";function f(e){if(void 0!==e)return"number"==typeof e?`${e}px`:e}export const Skeleton=r.forwardRef(function({shape:r="line",lines:h=1,size:c="md",color:x="secondary",width:g,height:p,animated:b=!0,label:w,render:y,className:$,style:v,...j},k){const N=b?"neba-skeleton":"",B="circle"===r?`rounded-full ${n[c]} ${d[c]} shrink-0`:"rect"===r?`w-full ${t[c]} ${void 0===p?i[c]:""}`:`w-full ${m[c]} ${a[c]}`,z=w?{role:"status","aria-busy":!0,"aria-label":w}:{"aria-hidden":!0},A="line"===r&&h>1;return l({render:y,ref:k,props:{className:(A?["flex w-full flex-col",s[c],$??""]:[u,N,B,$??""]).filter(Boolean).join(" "),style:{...o(x,0),width:f(g),height:f(p),...v},...z,...A?{children:Array.from({length:h},(r,l)=>e("div",{className:[u,N,m[c],a[c],l===h-1?"w-3/5":"w-full"].filter(Boolean).join(" ")},l))}:null,...j}})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Skeleton}from"./Skeleton";
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaColor, NebaDensity, NebaElevation, NebaSize, NebaVariant } from '../../types';
|
|
3
|
+
export interface SpoilerProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'onChange'> {
|
|
4
|
+
/** Whether the content is uncovered. Pass it to drive the Spoiler yourself. */
|
|
5
|
+
revealed?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Where an uncontrolled Spoiler starts.
|
|
8
|
+
* @default false
|
|
9
|
+
*/
|
|
10
|
+
defaultRevealed?: boolean;
|
|
11
|
+
/** Called when the reveal or hide button is pressed. */
|
|
12
|
+
onRevealedChange?: (revealed: boolean) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Which language the default label and notice are written in — a BCP 47 tag
|
|
15
|
+
* such as `ko`, `pt-BR` or `zh-Hant`. Unsupported tags fall back to English.
|
|
16
|
+
*
|
|
17
|
+
* Both strings can be written out instead, in `label` and `description`; this
|
|
18
|
+
* is for the far more common case where the page already knows its language
|
|
19
|
+
* and nobody should have to translate a button that says "Reveal".
|
|
20
|
+
*/
|
|
21
|
+
locale?: string;
|
|
22
|
+
/** The reveal button's label. Defaults to the `locale`'s word for it. */
|
|
23
|
+
label?: React.ReactNode;
|
|
24
|
+
/** The hide button's label, when `reversible` is on. */
|
|
25
|
+
hideLabel?: React.ReactNode;
|
|
26
|
+
/**
|
|
27
|
+
* The line above the button, saying why the content is covered. Defaults to
|
|
28
|
+
* the `locale`'s wording; pass `false` for a cover with nothing written on it.
|
|
29
|
+
*/
|
|
30
|
+
description?: React.ReactNode | false;
|
|
31
|
+
/**
|
|
32
|
+
* Replaces the default reveal button entirely.
|
|
33
|
+
*
|
|
34
|
+
* The replacement is yours to wire up: pass `revealed` and
|
|
35
|
+
* `onRevealedChange` and drive it from your own control. `label` is the prop
|
|
36
|
+
* for the far commoner case of wanting different words on the button that is
|
|
37
|
+
* already there.
|
|
38
|
+
*/
|
|
39
|
+
action?: React.ReactNode;
|
|
40
|
+
/**
|
|
41
|
+
* Keeps the content coverable: once revealed, a hide button appears under it.
|
|
42
|
+
* @default false
|
|
43
|
+
*/
|
|
44
|
+
reversible?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Clamps the covered box to this height — a CSS length, or a number in
|
|
47
|
+
* pixels. Revealing releases it and the content takes whatever height it
|
|
48
|
+
* needs.
|
|
49
|
+
*
|
|
50
|
+
* Left out, the box is exactly as tall as what it holds, which is the right
|
|
51
|
+
* default for a paragraph or a picture. Set it for something long enough that
|
|
52
|
+
* a page of blurred content would be a page of nothing.
|
|
53
|
+
*/
|
|
54
|
+
maxHeight?: number | string;
|
|
55
|
+
/**
|
|
56
|
+
* How hard the content is blurred, in pixels.
|
|
57
|
+
* @default 10
|
|
58
|
+
*/
|
|
59
|
+
blur?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Inner padding around the content, on Box's own `size`/`density` scale. Turn
|
|
62
|
+
* it off for something that should reach the edges — a picture, a video.
|
|
63
|
+
* @default true
|
|
64
|
+
*/
|
|
65
|
+
padded?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Weight of the box's surface. `text` draws no box at all, which is what a
|
|
68
|
+
* spoiler sitting inside running prose usually wants.
|
|
69
|
+
* @default 'outline'
|
|
70
|
+
*/
|
|
71
|
+
variant?: NebaVariant;
|
|
72
|
+
/** The sheet's radius, and the size of the button on it. @default 'md' */
|
|
73
|
+
size?: NebaSize;
|
|
74
|
+
/** @default 'primary' */
|
|
75
|
+
color?: NebaColor;
|
|
76
|
+
/** Padding around the cover's own text and button. @default 'default' */
|
|
77
|
+
density?: NebaDensity;
|
|
78
|
+
/** Drop shadow depth. `0` (the default) is flat. @default 0 */
|
|
79
|
+
elevation?: NebaElevation;
|
|
80
|
+
/** What is being covered. */
|
|
81
|
+
children?: React.ReactNode;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Content that is covered until somebody asks for it.
|
|
85
|
+
*
|
|
86
|
+
* The cover is a blur rather than a `display: none`, which is the whole point:
|
|
87
|
+
* a reader can see that there is something there, roughly how much of it there
|
|
88
|
+
* is, and — with `maxHeight` — that it has been clamped. What they cannot do is
|
|
89
|
+
* read it by accident, which is the one thing a spoiler is for.
|
|
90
|
+
*
|
|
91
|
+
* While it is covered the content is `inert`, so it is not tabbable, not
|
|
92
|
+
* readable by a screen reader and not selectable by a drag across the page. A
|
|
93
|
+
* spoiler that could be defeated by Ctrl-A is not a spoiler.
|
|
94
|
+
*/
|
|
95
|
+
export declare const Spoiler: React.ForwardRefExoticComponent<SpoilerProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as i from"react";import{Button as n}from"../button/Button";import{useMessages as o}from"../../internal/i18n";import{boxPaddingClasses as a,boxPaddingXClasses as l,boxPaddingYClasses as t}from"../box/Box";import{hasContent as s,metaTextClasses as d,radiusClasses as c,surfaceClasses as m,surfaceSlots as v,transitionClasses as f}from"../../internal/styles";const p={solid:[m,"bg-(--n-panel-hover)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[m,"border bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:"bg-transparent"},u="[background-color:color-mix(in_oklab,var(--neba-surface)_55%,transparent)]";export const Spoiler=i.forwardRef(function({revealed:m,defaultRevealed:b=!1,onRevealedChange:x,locale:h,label:j,hideLabel:g,description:y,action:w,reversible:N=!1,maxHeight:k,blur:z=10,padded:B=!0,variant:_="outline",size:C="md",color:R="primary",density:H="default",elevation:S=0,className:$,style:I,children:L,...q},A){const D=o(h),E=i.useId(),[F,G]=i.useState(b),J=m??F,K=e=>{void 0===m&&G(e),x?.(e)},M=!1===y?null:y??D.spoiler.notice;return r("div",{ref:A,className:["relative isolate overflow-hidden",c[C],p[_],f,$??""].filter(Boolean).join(" "),style:{...v(R,S),...I},...q,children:[e("div",{id:E,className:["min-w-0",B?a[H][C]:"","[transition:filter_var(--neba-duration-fill)_var(--neba-ease)]","motion-reduce:[transition-duration:0ms]",J?"":"select-none"].filter(Boolean).join(" "),style:{filter:J?void 0:`blur(${z}px)`,maxHeight:J?void 0:k,overflow:J?void 0:"hidden"},inert:!J,children:L}),J?null:r("div",{className:["absolute inset-0 z-10 flex flex-col items-center justify-center gap-2 text-center",a[H][C],u].join(" "),children:[s(M)?e("p",{className:`m-0 text-(--neba-muted-fg) ${d[C]}`,children:M}):null,w??e(n,{size:C,color:R,density:H,onClick:()=>K(!0),"aria-expanded":!1,"aria-controls":E,children:j??D.spoiler.reveal})]}),J&&N?e("div",{className:["flex justify-end",l[H][C],t[H][C],"pt-0"].join(" "),children:e(n,{variant:"text",size:C,color:R,density:H,onClick:()=>K(!1),"aria-expanded":!0,"aria-controls":E,children:g??D.spoiler.hide})}):null]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Spoiler}from"./Spoiler";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
+
import type { NebaColor, NebaSize } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* When the line under a link is drawn.
|
|
6
|
+
*
|
|
7
|
+
* `always` is the default, and the reason is `color`: a TextLink takes no
|
|
8
|
+
* colour family unless one is asked for, so with the line off there would be
|
|
9
|
+
* nothing at all distinguishing a link from the sentence it sits in. That is
|
|
10
|
+
* also why this is not a boolean — "no underline" is a real choice for a link
|
|
11
|
+
* in a nav bar or a footer, where position already says what it is, and it
|
|
12
|
+
* should have to be spelled out rather than fallen into.
|
|
13
|
+
*/
|
|
14
|
+
export type TextLinkUnderline = 'always' | 'hover' | 'none';
|
|
15
|
+
export interface TextLinkProps extends Omit<React.ComponentPropsWithoutRef<'a'>, 'color'> {
|
|
16
|
+
/** Where the link goes. */
|
|
17
|
+
href: string;
|
|
18
|
+
/**
|
|
19
|
+
* When the underline is drawn.
|
|
20
|
+
* @default 'always'
|
|
21
|
+
*/
|
|
22
|
+
underline?: TextLinkUnderline;
|
|
23
|
+
/**
|
|
24
|
+
* Semantic colour role. Like [Typography](../display/typography) and unlike
|
|
25
|
+
* every control in the library this has **no default** — a link in a
|
|
26
|
+
* paragraph is usually the paragraph's own colour with a line under it, and a
|
|
27
|
+
* component that arrived pre-dyed is one a page has to undo.
|
|
28
|
+
*/
|
|
29
|
+
color?: NebaColor;
|
|
30
|
+
/**
|
|
31
|
+
* The type scale. Also no default: a link inside a sentence is the size of
|
|
32
|
+
* the sentence. Set it for a link that stands on its own.
|
|
33
|
+
*/
|
|
34
|
+
size?: NebaSize;
|
|
35
|
+
/**
|
|
36
|
+
* Opens the link in a new tab, with the `rel` that keeps the new page from
|
|
37
|
+
* reaching back into this one.
|
|
38
|
+
*
|
|
39
|
+
* A window changing under the reader is the one thing about a link that
|
|
40
|
+
* cannot be seen before it happens, so this also turns `icon` on by default
|
|
41
|
+
* and adds a line for a screen reader — see `locale`.
|
|
42
|
+
* @default false
|
|
43
|
+
*/
|
|
44
|
+
newTab?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* The mark after the label. `true` draws the arrow leaving its box when
|
|
47
|
+
* `newTab` is on and the chain otherwise, `false` draws nothing, and a node
|
|
48
|
+
* of your own replaces the glyph.
|
|
49
|
+
*
|
|
50
|
+
* Left out, it follows `newTab`.
|
|
51
|
+
*/
|
|
52
|
+
icon?: React.ReactNode | boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Which language the line for a screen reader is written in — a BCP 47 tag
|
|
55
|
+
* such as `ko`, `pt-BR` or `zh-Hant`. Unsupported tags fall back to English.
|
|
56
|
+
*
|
|
57
|
+
* Only `newTab` produces that line; a link without it says nothing of its
|
|
58
|
+
* own, so this changes nothing.
|
|
59
|
+
*/
|
|
60
|
+
locale?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Renders something other than an `<a>` — the `Link` a router brings, most
|
|
63
|
+
* of the time. `href` still goes through, so `render={<NextLink href="…" />}`
|
|
64
|
+
* needs it written once, on the TextLink.
|
|
65
|
+
*/
|
|
66
|
+
render?: useRender.RenderProp;
|
|
67
|
+
/** The label. */
|
|
68
|
+
children?: React.ReactNode;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A link, in a sentence or on its own.
|
|
72
|
+
*
|
|
73
|
+
* Everything about it is deliberately smaller than a Button. It has no surface,
|
|
74
|
+
* no height of its own and no colour unless asked — what it has is a line under
|
|
75
|
+
* it, which is the one mark a reader already knows means "this goes somewhere".
|
|
76
|
+
*
|
|
77
|
+
* The three things it does that a bare `<a>` does not: it draws that line on a
|
|
78
|
+
* schedule (`always`, or only under the pointer), it marks a link that opens a
|
|
79
|
+
* new tab both visibly and for a screen reader, and it takes `render`, so the
|
|
80
|
+
* `Link` a router brings can wear all of it.
|
|
81
|
+
*/
|
|
82
|
+
export declare const TextLink: React.ForwardRefExoticComponent<TextLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as n,Fragment as e,jsxs as r}from"react/jsx-runtime";import*as i from"react";import{useRender as o}from"@base-ui/react/use-render";import{useMessages as a}from"../../internal/i18n";import{ExternalLinkIcon as t,LinkIcon as l}from"../../internal/icons";import{controlTextLeadingClasses as s,focusRingClasses as c,srOnlyClasses as m}from"../../internal/styles";const d={always:"[&.neba-link]:underline",hover:"[&.neba-link]:no-underline [&.neba-link]:hover:underline",none:"[&.neba-link]:no-underline"},u=["neba-link","cursor-pointer","[&_svg]:pointer-events-none [&_svg]:inline [&_svg]:size-[0.95em] [&_svg]:shrink-0",["[transition-property:color,text-decoration-color]","[transition-duration:var(--neba-duration)]","[transition-timing-function:var(--neba-ease)]"].join(" "),c,"focus-visible:rounded-[0.25rem]"].join(" ");export const TextLink=i.forwardRef(function({href:i,underline:c="always",color:p,size:b,newTab:f=!1,icon:v,locale:h,render:k,className:g,style:x,children:y,...j},w){const _=a(h),N=v??f,T=!0===N?n(f?t:l,{}):N,z=[u,b?s[b]:"",d[c],p?"[&.neba-link]:text-(--n-accent)":"[&.neba-link]:text-inherit",g??""].filter(Boolean).join(" "),C={"--n-underline":"color-mix(in oklab, currentColor 45%, transparent)","--n-underline-hover":"currentColor","--n-ring":`var(--neba-${p??"primary"}-ring)`,...p?{"--n-accent":`var(--neba-${p}-accent)`}:null};return o({render:k??n("a",{}),ref:w,props:{href:i,target:f?"_blank":void 0,rel:f?"noopener noreferrer":void 0,className:z,style:{...C,...x},children:r(e,{children:[y,T?n("span",{className:"ms-[0.25em]",children:T}):null,f?r(e,{children:[" ",n("span",{className:m,children:_.link.newTab})]}):null]}),...j}})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{TextLink}from"./TextLink";
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* How the hierarchy is drawn.
|
|
5
|
+
*
|
|
6
|
+
* - `none` — indentation only. The quietest, and usually right for a navigation
|
|
7
|
+
* sidebar, where the tree is two levels deep and the labels say what the
|
|
8
|
+
* structure is.
|
|
9
|
+
* - `simple` — one hairline rail per level, running the full height of the
|
|
10
|
+
* group. Enough to follow a column of rows back to the branch it belongs to.
|
|
11
|
+
* - `folder` — the rail plus an elbow into every row, and the rail under a last
|
|
12
|
+
* child stops at that row. The file-manager drawing, and the one to reach for
|
|
13
|
+
* when the tree is deep.
|
|
14
|
+
*/
|
|
15
|
+
export type TreeViewLines = 'none' | 'simple' | 'folder';
|
|
16
|
+
/** A row's identity, and what `expanded` and `selected` are lists of. */
|
|
17
|
+
export type TreeViewValue = string | number;
|
|
18
|
+
export interface TreeViewProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'ul'>, 'color' | 'defaultValue' | 'onSelect'> {
|
|
19
|
+
/**
|
|
20
|
+
* Drop shadow depth. `0` (the default) is flat.
|
|
21
|
+
* @default 0
|
|
22
|
+
*/
|
|
23
|
+
elevation?: NebaElevation;
|
|
24
|
+
/**
|
|
25
|
+
* How the hierarchy is drawn between the rows.
|
|
26
|
+
* @default 'simple'
|
|
27
|
+
*/
|
|
28
|
+
lines?: TreeViewLines;
|
|
29
|
+
/** Which branches are open. Use with `onExpandedChange` for a controlled tree. */
|
|
30
|
+
expanded?: TreeViewValue[];
|
|
31
|
+
/** Which start open, for an uncontrolled one. */
|
|
32
|
+
defaultExpanded?: TreeViewValue[];
|
|
33
|
+
onExpandedChange?: (expanded: TreeViewValue[]) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Which rows are chosen. An array even when only one row may be chosen at a
|
|
36
|
+
* time — the same shape Accordion's `value` takes, so turning `multiple` on
|
|
37
|
+
* does not also change the type of the value.
|
|
38
|
+
*/
|
|
39
|
+
selected?: TreeViewValue[];
|
|
40
|
+
/** Which start chosen, for an uncontrolled one. */
|
|
41
|
+
defaultSelected?: TreeViewValue[];
|
|
42
|
+
onSelectedChange?: (selected: TreeViewValue[]) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Whether more than one row may be chosen at a time.
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
multiple?: boolean;
|
|
48
|
+
/** Unavailable. Every row stops answering. */
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
/** The name the tree is announced by. */
|
|
51
|
+
label?: string;
|
|
52
|
+
/** The top-level TreeItems. */
|
|
53
|
+
children?: React.ReactNode;
|
|
54
|
+
}
|
|
55
|
+
export interface TreeItemProps extends Omit<React.ComponentPropsWithoutRef<'li'>, 'color' | 'onClick'> {
|
|
56
|
+
/**
|
|
57
|
+
* Identifies the row to `expanded` and `selected`. One is generated when it is
|
|
58
|
+
* left out, which is fine for a tree nobody drives from code.
|
|
59
|
+
*/
|
|
60
|
+
value?: TreeViewValue;
|
|
61
|
+
/**
|
|
62
|
+
* The row's text. Its own prop rather than `children`, because in a tree the
|
|
63
|
+
* children are the rows underneath it.
|
|
64
|
+
*/
|
|
65
|
+
label?: React.ReactNode;
|
|
66
|
+
/** Content before the label — a folder glyph, a file type, a status dot. */
|
|
67
|
+
startIcon?: React.ReactNode;
|
|
68
|
+
/** Content after the label, inside the pressable area — a count, a badge. */
|
|
69
|
+
endIcon?: React.ReactNode;
|
|
70
|
+
/**
|
|
71
|
+
* A control pinned to the end of the row.
|
|
72
|
+
*
|
|
73
|
+
* Deliberately outside the pressable area, the same shape ListItem uses: a
|
|
74
|
+
* row that both opens and holds a menu button has two things to press.
|
|
75
|
+
*/
|
|
76
|
+
action?: React.ReactNode;
|
|
77
|
+
/** Renders the row as a link, for a tree that is navigation. */
|
|
78
|
+
href?: string;
|
|
79
|
+
/** Fires when the row is pressed, before it opens or is chosen. */
|
|
80
|
+
onClick?: React.MouseEventHandler<HTMLElement>;
|
|
81
|
+
/**
|
|
82
|
+
* Forces the disclosure arrow onto a row with no children yet — the branch
|
|
83
|
+
* that is fetched the first time it is opened.
|
|
84
|
+
*/
|
|
85
|
+
expandable?: boolean;
|
|
86
|
+
/** Unavailable. Its branch, if it is open, keeps working. */
|
|
87
|
+
disabled?: boolean;
|
|
88
|
+
/** The rows underneath this one. */
|
|
89
|
+
children?: React.ReactNode;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A tree of rows that open and shut.
|
|
93
|
+
*
|
|
94
|
+
* There is no Base UI primitive under this: the library has no tree, and the
|
|
95
|
+
* three things a tree owes — the `tree`/`treeitem`/`group` roles, one tab stop
|
|
96
|
+
* for the whole widget, and the arrow keys that walk it — are most of the
|
|
97
|
+
* component.
|
|
98
|
+
*
|
|
99
|
+
* The keyboard is handled once, here, rather than on every row. A tree's arrow
|
|
100
|
+
* keys are questions about the *tree* ("what is the next visible row", "where is
|
|
101
|
+
* my parent"), and the only element that can answer them is the one holding all
|
|
102
|
+
* of them. The rows the query returns are in document order, which is reading
|
|
103
|
+
* order, because a shut branch is unmounted rather than hidden.
|
|
104
|
+
*/
|
|
105
|
+
export declare const TreeView: React.ForwardRefExoticComponent<TreeViewProps & React.RefAttributes<HTMLUListElement>>;
|
|
106
|
+
/**
|
|
107
|
+
* One row, and everything under it.
|
|
108
|
+
*
|
|
109
|
+
* The `<li>` is the `treeitem` and it is what takes focus — not the row drawn
|
|
110
|
+
* inside it. That is the ARIA pattern, and it is also why the disclosure arrow
|
|
111
|
+
* is a plain span with a click handler rather than a button: a button inside the
|
|
112
|
+
* focusable row would be a second tab stop in a widget that is supposed to have
|
|
113
|
+
* exactly one, and the keyboard already opens a branch with ArrowRight.
|
|
114
|
+
*/
|
|
115
|
+
export declare const TreeItem: React.ForwardRefExoticComponent<TreeItemProps & React.RefAttributes<HTMLLIElement>>;
|