neba 1.0.0 → 1.2.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 +27 -16
- package/dist/components/avatar/Avatar.d.ts +100 -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 +65 -0
- package/dist/components/blockquote/Blockquote.js +1 -0
- package/dist/components/blockquote/index.d.ts +2 -0
- package/dist/components/blockquote/index.js +1 -0
- 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/carousel/Carousel.d.ts +74 -0
- package/dist/components/carousel/Carousel.js +1 -0
- package/dist/components/carousel/index.d.ts +2 -0
- package/dist/components/carousel/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/checkbox/Checkbox.js +1 -1
- package/dist/components/combobox/Combobox.js +1 -1
- package/dist/components/container/Container.d.ts +55 -0
- package/dist/components/container/Container.js +1 -0
- package/dist/components/container/index.d.ts +2 -0
- package/dist/components/container/index.js +1 -0
- package/dist/components/date-picker/DatePicker.d.ts +75 -0
- package/dist/components/date-picker/DatePicker.js +1 -0
- package/dist/components/date-picker/index.d.ts +2 -0
- package/dist/components/date-picker/index.js +1 -0
- package/dist/components/date-range-picker/DateRangePicker.d.ts +81 -0
- package/dist/components/date-range-picker/DateRangePicker.js +1 -0
- package/dist/components/date-range-picker/index.d.ts +2 -0
- package/dist/components/date-range-picker/index.js +1 -0
- package/dist/components/date-time-picker/DateTimePicker.d.ts +68 -0
- package/dist/components/date-time-picker/DateTimePicker.js +1 -0
- package/dist/components/date-time-picker/index.d.ts +2 -0
- package/dist/components/date-time-picker/index.js +1 -0
- package/dist/components/dialog/Dialog.js +1 -1
- package/dist/components/divider/Divider.d.ts +19 -0
- package/dist/components/divider/Divider.js +1 -1
- package/dist/components/grid/Grid.d.ts +50 -0
- package/dist/components/grid/Grid.js +1 -0
- package/dist/components/grid/index.d.ts +2 -0
- package/dist/components/grid/index.js +1 -0
- package/dist/components/grid-container/GridContainer.d.ts +79 -0
- package/dist/components/grid-container/GridContainer.js +1 -0
- package/dist/components/grid-container/index.d.ts +2 -0
- package/dist/components/grid-container/index.js +1 -0
- package/dist/components/highlight/Highlight.d.ts +84 -0
- package/dist/components/highlight/Highlight.js +1 -0
- package/dist/components/highlight/index.d.ts +2 -0
- package/dist/components/highlight/index.js +1 -0
- package/dist/components/icon/Icon.d.ts +58 -0
- package/dist/components/icon/Icon.js +1 -0
- package/dist/components/icon/index.d.ts +2 -0
- package/dist/components/icon/index.js +1 -0
- package/dist/components/icon-button/IconButton.d.ts +39 -0
- package/dist/components/icon-button/IconButton.js +1 -0
- package/dist/components/icon-button/index.d.ts +2 -0
- package/dist/components/icon-button/index.js +1 -0
- 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/pagination/Pagination.js +1 -1
- 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/pill/Pill.d.ts +80 -0
- package/dist/components/pill/Pill.js +1 -0
- package/dist/components/pill/index.d.ts +2 -0
- package/dist/components/pill/index.js +1 -0
- package/dist/components/radio-group/RadioGroup.js +1 -1
- package/dist/components/segmented-button/SegmentedButton.d.ts +77 -0
- package/dist/components/segmented-button/SegmentedButton.js +1 -0
- package/dist/components/segmented-button/index.d.ts +2 -0
- package/dist/components/segmented-button/index.js +1 -0
- package/dist/components/select/Select.js +1 -1
- package/dist/components/shortcut/Shortcut.d.ts +60 -0
- package/dist/components/shortcut/Shortcut.js +1 -0
- package/dist/components/shortcut/index.d.ts +2 -0
- package/dist/components/shortcut/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/statistic/Statistic.d.ts +91 -0
- package/dist/components/statistic/Statistic.js +1 -0
- package/dist/components/statistic/index.d.ts +2 -0
- package/dist/components/statistic/index.js +1 -0
- package/dist/components/switch/Switch.js +1 -1
- 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/time-picker/TimePicker.d.ts +76 -0
- package/dist/components/time-picker/TimePicker.js +1 -0
- package/dist/components/time-picker/index.d.ts +2 -0
- package/dist/components/time-picker/index.js +1 -0
- package/dist/components/timeline/Timeline.d.ts +98 -0
- package/dist/components/timeline/Timeline.js +1 -0
- package/dist/components/timeline/index.d.ts +2 -0
- package/dist/components/timeline/index.js +1 -0
- package/dist/components/toolbar/Toolbar.d.ts +67 -0
- package/dist/components/toolbar/Toolbar.js +1 -0
- package/dist/components/toolbar/index.d.ts +2 -0
- package/dist/components/toolbar/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/index.d.ts +26 -0
- package/dist/index.js +1 -1
- package/dist/internal/calendar.d.ts +149 -0
- package/dist/internal/calendar.js +1 -0
- package/dist/internal/date.d.ts +170 -0
- package/dist/internal/date.js +1 -0
- package/dist/internal/grid.d.ts +69 -0
- package/dist/internal/grid.js +1 -0
- package/dist/internal/i18n.d.ts +78 -0
- package/dist/internal/i18n.js +1 -0
- package/dist/internal/icons.d.ts +54 -0
- package/dist/internal/icons.js +1 -1
- package/dist/internal/picker.d.ts +111 -0
- package/dist/internal/picker.js +1 -0
- package/dist/internal/sizer.d.ts +33 -0
- package/dist/internal/sizer.js +1 -0
- package/dist/internal/styles.d.ts +33 -0
- package/dist/internal/styles.js +1 -1
- package/dist/styles.css +2 -758
- package/dist/tailwind.css +1017 -0
- package/dist/types.d.ts +65 -0
- package/package.json +25 -12
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
+
import type { NebaAlignItems, NebaDensity, NebaJustifyContent, NebaResponsive, NebaSize } from '../../types';
|
|
4
|
+
export interface GridContainerProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
5
|
+
/**
|
|
6
|
+
* How many columns a row is divided into. Every `span` and every `offset`
|
|
7
|
+
* inside is read against this number, so `columns={24}` makes `span={12}` a
|
|
8
|
+
* half and not a full width.
|
|
9
|
+
* @default 12
|
|
10
|
+
*/
|
|
11
|
+
columns?: NebaResponsive<number>;
|
|
12
|
+
/**
|
|
13
|
+
* The gutter between items, on Tailwind's spacing scale — `spacing={4}` is
|
|
14
|
+
* `1rem`, the same length `gap-4` is. Fractions are allowed, so `1.5` is
|
|
15
|
+
* `0.375rem`.
|
|
16
|
+
* @default 2
|
|
17
|
+
*/
|
|
18
|
+
spacing?: NebaResponsive<number>;
|
|
19
|
+
/** The gutter between rows only. Falls back to `spacing`. */
|
|
20
|
+
rowSpacing?: NebaResponsive<number>;
|
|
21
|
+
/** The gutter between columns only. Falls back to `spacing`. */
|
|
22
|
+
columnSpacing?: NebaResponsive<number>;
|
|
23
|
+
/** How a row distributes the space its items did not use. */
|
|
24
|
+
justifyContent?: NebaJustifyContent;
|
|
25
|
+
/** How items sit against each other across the row. @default 'stretch' */
|
|
26
|
+
alignItems?: NebaAlignItems;
|
|
27
|
+
/** Where the rows sit when the grid is shorter than the box holding it. */
|
|
28
|
+
alignContent?: NebaJustifyContent;
|
|
29
|
+
/**
|
|
30
|
+
* Whether a row that runs out of columns continues on the next one. Turning
|
|
31
|
+
* it off gives one row that overflows, which is what a horizontally
|
|
32
|
+
* scrolling strip wants.
|
|
33
|
+
* @default true
|
|
34
|
+
*/
|
|
35
|
+
wrap?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Inner padding, on the `size`/`density` scale. Turn it off when the grid is
|
|
38
|
+
* already inside something that pads — a Container, a Card, another grid.
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
padded?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* The padding scale. As on Box, this is the size of the *sheet* — it never
|
|
44
|
+
* touches a height or the type scale, and it has nothing to do with the
|
|
45
|
+
* gutters, which are `spacing`.
|
|
46
|
+
* @default 'md'
|
|
47
|
+
*/
|
|
48
|
+
size?: NebaSize;
|
|
49
|
+
/** @default 'default' */
|
|
50
|
+
density?: NebaDensity;
|
|
51
|
+
/**
|
|
52
|
+
* Renders something other than a `<div>`: `render={<section />}`,
|
|
53
|
+
* `render={<ul />}`. Base UI's own escape hatch.
|
|
54
|
+
*/
|
|
55
|
+
render?: useRender.RenderProp;
|
|
56
|
+
children?: React.ReactNode;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The parent every `Grid` needs.
|
|
60
|
+
*
|
|
61
|
+
* It owns the three numbers an item cannot know on its own — how many columns
|
|
62
|
+
* there are and how wide the two gutters are — and hands them down as inherited
|
|
63
|
+
* custom properties rather than through a React context. That is not a
|
|
64
|
+
* shortcut: the values are responsive, and a media query can change an
|
|
65
|
+
* inherited custom property without React hearing about it, so the column count
|
|
66
|
+
* an item lays itself out against is always the one that is on screen. A
|
|
67
|
+
* context would have to re-render the tree at every breakpoint to say the same
|
|
68
|
+
* thing.
|
|
69
|
+
*
|
|
70
|
+
* It deliberately does not take `variant`, `color` or `elevation`. A grid is
|
|
71
|
+
* not a surface — it is the arrangement of the surfaces inside it, and the
|
|
72
|
+
* moment it draws its own sheet it stops being usable as the outermost thing on
|
|
73
|
+
* a page. Wrap it in a Box or a Card when the sheet is wanted.
|
|
74
|
+
*
|
|
75
|
+
* Nesting is a `GridContainer` inside a `Grid`, not a `Grid` that is also a
|
|
76
|
+
* container: the inner grid re-declares the column count for its own subtree
|
|
77
|
+
* while the item around it keeps the width the outer grid gave it.
|
|
78
|
+
*/
|
|
79
|
+
export declare const GridContainer: React.ForwardRefExoticComponent<GridContainerProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"react";import{useRender as r}from"@base-ui/react/use-render";import{alignContentClasses as n,alignItemsClasses as a,columnCount as o,justifyContentClasses as t,responsiveSlots as i,spacingValue as p,withBaseline as s}from"../../internal/grid";import{boxPaddingClasses as l}from"../box/Box";export const GridContainer=e.forwardRef(function({columns:e,spacing:c,rowSpacing:d,columnSpacing:f,justifyContent:m,alignItems:g,alignContent:x,wrap:u=!0,padded:y=!0,size:w="md",density:b="default",render:C,className:h,style:j,children:B,...N},S){const z=["neba-grid flex",u?"flex-wrap":"flex-nowrap","gap-x-(--n-gap-x) gap-y-(--n-gap-y)",y?l[b][w]:"",m?t[m]:"",g?a[g]:"",x?n[x]:"",h??""].filter(Boolean).join(" ");return r({render:C,ref:S,props:{className:z,style:{...i("cols",s(e,12),o),...i("gap-x",s(f??c,2),p),...i("gap-y",s(d??c,2),p),...j},children:B,...N}})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{GridContainer}from"./GridContainer";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { TypographyWeight } from '../typography/Typography';
|
|
3
|
+
import type { NebaColor, NebaVariant } from '../../types';
|
|
4
|
+
export interface HighlightProps extends Omit<React.ComponentPropsWithoutRef<'span'>, 'color'> {
|
|
5
|
+
/**
|
|
6
|
+
* What to find.
|
|
7
|
+
*
|
|
8
|
+
* A string is one term, an array is several — the longest is tried first, so
|
|
9
|
+
* `['data', 'database']` marks the whole word rather than the first four
|
|
10
|
+
* letters of it. A `RegExp` is used as written, with the global flag forced
|
|
11
|
+
* on; `caseSensitive` and `wholeWord` are ignored for it, because a regular
|
|
12
|
+
* expression already says both of those things itself.
|
|
13
|
+
*/
|
|
14
|
+
query: string | string[] | RegExp;
|
|
15
|
+
/**
|
|
16
|
+
* Weight of the mark.
|
|
17
|
+
*
|
|
18
|
+
* - `solid` — filled, the highlighter pen. The default.
|
|
19
|
+
* - `outline` — a hairline box around the word, for a page where a filled run
|
|
20
|
+
* would be too much.
|
|
21
|
+
* - `text` — the accent colour and nothing else, for marking a word inside a
|
|
22
|
+
* heading that is already loud.
|
|
23
|
+
* @default 'solid'
|
|
24
|
+
*/
|
|
25
|
+
variant?: NebaVariant;
|
|
26
|
+
/**
|
|
27
|
+
* Semantic colour role. `warning` by default, and not arbitrarily: it is the
|
|
28
|
+
* one family whose fill is light with dark ink on it, so a solid `warning`
|
|
29
|
+
* mark is a yellow highlighter over black text rather than a white word on a
|
|
30
|
+
* block of colour.
|
|
31
|
+
* @default 'warning'
|
|
32
|
+
*/
|
|
33
|
+
color?: NebaColor;
|
|
34
|
+
/**
|
|
35
|
+
* Whether `a` and `A` are different letters.
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
caseSensitive?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Whether a term has to be a word on its own — `cat` marking "cat" but not
|
|
41
|
+
* "concatenate".
|
|
42
|
+
*
|
|
43
|
+
* A word here is a run of letters, digits and underscores in any script, so it
|
|
44
|
+
* means what it should for `café` and `naïve`. It means very little for Korean
|
|
45
|
+
* or Japanese, where a phrase is not delimited by spaces at all; that is a
|
|
46
|
+
* property of the writing system rather than of this prop, and is the reason
|
|
47
|
+
* it is off by default.
|
|
48
|
+
* @default false
|
|
49
|
+
*/
|
|
50
|
+
wholeWord?: boolean;
|
|
51
|
+
/** Underlines the mark as well. Combines with every variant. */
|
|
52
|
+
underline?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Sets the mark's weight. Omit it and the mark is the weight of the text
|
|
55
|
+
* around it — the surface is already saying "this one", and a bolded word
|
|
56
|
+
* inside a sentence changes the rhythm of the whole line.
|
|
57
|
+
*/
|
|
58
|
+
weight?: TypographyWeight;
|
|
59
|
+
/**
|
|
60
|
+
* The text to search. Elements are walked into and left otherwise untouched,
|
|
61
|
+
* so a match inside a `<strong>` is still marked and the `<strong>` survives.
|
|
62
|
+
*/
|
|
63
|
+
children?: React.ReactNode;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Marks the words a reader is looking for, inside text they were already
|
|
67
|
+
* reading.
|
|
68
|
+
*
|
|
69
|
+
* The component is the search, not just the styling: `query` is what a search
|
|
70
|
+
* box holds, and everything about *how* the matching is done — case, whole
|
|
71
|
+
* words, a regular expression — is a prop rather than something a caller has to
|
|
72
|
+
* pre-compute into a list of offsets.
|
|
73
|
+
*
|
|
74
|
+
* The mark is a real `<mark>`, which is the element for "text of relevance to
|
|
75
|
+
* the reader" and is announced as such. That has one consequence worth knowing:
|
|
76
|
+
* marking eleven words in a paragraph tells a screen reader that eleven things
|
|
77
|
+
* are important, which is a way of saying nothing. A highlight is for a handful
|
|
78
|
+
* of matches.
|
|
79
|
+
*
|
|
80
|
+
* Nothing here is stateful and nothing measures — the whole component is a pure
|
|
81
|
+
* function of `children` and `query`, so it re-marks on its own the moment the
|
|
82
|
+
* search box changes.
|
|
83
|
+
*/
|
|
84
|
+
export declare const Highlight: React.ForwardRefExoticComponent<HighlightProps & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as n}from"react/jsx-runtime";import*as e from"react";import{transitionClasses as t}from"../../internal/styles";const r={solid:"bg-(--n-fill) text-(--n-on-solid)",outline:"border bg-(--n-panel) text-(--n-accent) [border-color:var(--n-line)]",text:"bg-transparent text-(--n-accent)"},o={regular:"font-normal",medium:"font-medium",semibold:"font-semibold",bold:"font-bold"},l=/[\p{L}\p{N}_]/u;function i(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function a(n,e,t){const r=e>0?n[e-1]:"",o=t<n.length?n[t]:"";return!l.test(r)&&!l.test(o)}function s(n,e,t,r){e.lastIndex=0;const o=[];let l,i=0;for(;null!==(l=e.exec(n));){if(""===l[0]){e.lastIndex+=1;continue}const s=l.index,c=s+l[0].length;t&&!a(n,s,c)||(s>i&&o.push(n.slice(i,s)),o.push(r(l[0],`${s}`)),i=c)}return 0===o.length?n:(i<n.length&&o.push(n.slice(i)),o)}function c(n,t,r,o){if("string"==typeof n)return s(n,t,r,o);if("number"==typeof n)return s(String(n),t,r,o);if(Array.isArray(n))return e.Children.map(n,n=>c(n,t,r,o));if(e.isValidElement(n)){const l=n.props.children;return void 0===l||"function"==typeof l?n:e.cloneElement(n,void 0,c(l,t,r,o))}return n}export const Highlight=e.forwardRef(function({query:l,variant:a="solid",color:s="warning",caseSensitive:u=!1,wholeWord:f=!1,underline:d=!1,weight:m,className:p,style:g,children:h,...b},x){const y=e.useMemo(()=>function(n,e){if(n instanceof RegExp)return n.global?n:new RegExp(n.source,`${n.flags}g`);const t=(Array.isArray(n)?n:[n]).map(n=>n.trim()).filter(Boolean).sort((n,e)=>e.length-n.length);return 0===t.length?null:new RegExp(t.map(i).join("|"),e?"gu":"giu")}(l,u),[l,u]),v=["rounded-[0.25rem] px-0.5 -mx-0.5","box-decoration-clone",r[a],d?"underline decoration-2 underline-offset-2":"",m?o[m]:"",t].filter(Boolean).join(" "),$=y?c(h,y,f,(e,t)=>n("mark",{className:v,children:e},t)):h;return n("span",{ref:x,className:p,style:{"--n-fill":`var(--neba-${s}-fill)`,"--n-on-solid":`var(--neba-${s}-on-solid)`,"--n-accent":`var(--neba-${s}-accent)`,"--n-panel":`var(--neba-${s}-panel)`,"--n-line":`var(--neba-${s}-line)`,...g},...b,children:$})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Highlight}from"./Highlight";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaColor, NebaSize } from '../../types';
|
|
3
|
+
export interface IconProps extends Omit<React.ComponentPropsWithoutRef<'span'>, 'color'> {
|
|
4
|
+
/**
|
|
5
|
+
* The glyph. An `<svg>` element, an `<img>`, a component from an icon set, or
|
|
6
|
+
* a character — whatever is passed is laid into a box of the right size and
|
|
7
|
+
* given a colour to inherit.
|
|
8
|
+
*
|
|
9
|
+
* It is a prop rather than `children` on purpose. An icon set hands you an
|
|
10
|
+
* element you did not draw, and the two things you always want to change about
|
|
11
|
+
* it — how big it is and what colour it is — are the two things you cannot
|
|
12
|
+
* reach once it is a child of something. As a prop it is content the Icon
|
|
13
|
+
* *sizes*, not content the Icon merely wraps.
|
|
14
|
+
*/
|
|
15
|
+
icon: React.ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* The box the glyph is drawn in: 14, 16, 20, 24 and 28px. Its own ladder
|
|
18
|
+
* rather than the control heights, because an icon is not a control.
|
|
19
|
+
* @default 'md'
|
|
20
|
+
*/
|
|
21
|
+
size?: NebaSize;
|
|
22
|
+
/**
|
|
23
|
+
* Semantic colour role, or `inherit` — the default — to take the colour of
|
|
24
|
+
* whatever the icon sits in.
|
|
25
|
+
*
|
|
26
|
+
* `inherit` is the default and not `primary`, which is the one place this prop
|
|
27
|
+
* deviates from every other component in the library. An icon is content, and
|
|
28
|
+
* the overwhelmingly common case is an icon inside something that has already
|
|
29
|
+
* decided what colour its content is: a Button's label, a muted caption, an
|
|
30
|
+
* Alert's own family. An Icon that arrived pre-dyed blue would have to be
|
|
31
|
+
* turned off again at every one of those.
|
|
32
|
+
* @default 'inherit'
|
|
33
|
+
*/
|
|
34
|
+
color?: NebaColor | 'inherit';
|
|
35
|
+
/**
|
|
36
|
+
* What the icon says, for a reader who cannot see it.
|
|
37
|
+
*
|
|
38
|
+
* Without it the icon is hidden from the accessibility tree entirely, which is
|
|
39
|
+
* the right default: the overwhelming majority of icons sit next to a word
|
|
40
|
+
* that already says the same thing, and reading both out loud is worse than
|
|
41
|
+
* reading one. Pass this only when the glyph is carrying meaning on its own.
|
|
42
|
+
*/
|
|
43
|
+
label?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A glyph at a known size, in a known colour.
|
|
47
|
+
*
|
|
48
|
+
* The library draws no icons of its own beyond the handful its components need —
|
|
49
|
+
* an icon set is a font-sized decision that belongs to the app, not to the
|
|
50
|
+
* component library it installs. What this does is give whatever icon the app
|
|
51
|
+
* chose the same two axes everything else here has.
|
|
52
|
+
*
|
|
53
|
+
* The box is `inline-flex` with the glyph told to fill it, and `font-size` is
|
|
54
|
+
* set to the same length — so an `<svg>` with its own `width`, an `<svg>` sized
|
|
55
|
+
* in `em`, and a bare character all come out the same size. That is the whole
|
|
56
|
+
* component: a box, a length written twice, and a colour.
|
|
57
|
+
*/
|
|
58
|
+
export declare const Icon: React.ForwardRefExoticComponent<IconProps & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import*as t from"react";import{iconSizeClasses as i}from"../../internal/styles";const n={xs:"text-[0.875rem]",sm:"text-[1rem]",md:"text-[1.25rem]",lg:"text-[1.5rem]",xl:"text-[1.75rem]"};export const Icon=t.forwardRef(function({icon:t,size:r="md",color:l="inherit",label:s,className:a,style:o,...c},m){const f=["inline-flex shrink-0 items-center justify-center align-middle",i[r],"[&>svg]:block [&>svg]:size-full [&>img]:block [&>img]:size-full",`leading-none ${n[r]}`,"inherit"===l?"":"text-(--n-accent)",a??""].filter(Boolean).join(" ");return e("span",{ref:m,className:f,style:"inherit"===l?o:{"--n-accent":`var(--neba-${l}-accent)`,...o},role:s?"img":void 0,"aria-label":s,"aria-hidden":!s||void 0,...c,children:t})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Icon}from"./Icon";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type ButtonProps } from '../button/Button';
|
|
3
|
+
export interface IconButtonProps extends Omit<ButtonProps, 'children' | 'startIcon' | 'endIcon'> {
|
|
4
|
+
/**
|
|
5
|
+
* The glyph. Wrap it in an `Icon` when it needs a size of its own; passed bare
|
|
6
|
+
* it is sized in `em` against the button, exactly as a `startIcon` is.
|
|
7
|
+
*/
|
|
8
|
+
icon: React.ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* What the button does, in words.
|
|
11
|
+
*
|
|
12
|
+
* Required, and the one prop here that is. A button whose whole label is a
|
|
13
|
+
* drawing has no accessible name at all, and "an icon button with no
|
|
14
|
+
* `aria-label`" is the single most common accessibility defect a component
|
|
15
|
+
* library ships. Making it required is the only fix that survives review.
|
|
16
|
+
*/
|
|
17
|
+
label: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A round button with a glyph in it and nothing else.
|
|
21
|
+
*
|
|
22
|
+
* A [Button] with an icon and no children already goes square — the same height,
|
|
23
|
+
* the same width, the acrylic corners cut off it. This is the other shape: a
|
|
24
|
+
* disc.
|
|
25
|
+
*
|
|
26
|
+
* That disc is a deliberate exception to the house radius rule, which holds
|
|
27
|
+
* every corner just short of the 50% that would make a control a pill, because
|
|
28
|
+
* the flat run along the top and bottom edge is what still reads as a sheet with
|
|
29
|
+
* its corners cut. The rule is about *labelled* controls: the flat run is there
|
|
30
|
+
* for the line of text to sit on, and a glyph has no line of text. A circle with
|
|
31
|
+
* a single mark centred in it is a punched token rather than a moulded key, so
|
|
32
|
+
* it says the thing the rule exists to protect by a different route.
|
|
33
|
+
*
|
|
34
|
+
* Everything else is Button's, unchanged and on purpose: the variants, the
|
|
35
|
+
* elevation ladder, the pointer light, `loading`, `readOnly`, and the values a
|
|
36
|
+
* surrounding ButtonGroup sets. Two components that draw the same surface from
|
|
37
|
+
* two copies of the same table are two components that will eventually disagree.
|
|
38
|
+
*/
|
|
39
|
+
export declare const IconButton: React.ForwardRefExoticComponent<IconButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as t}from"react/jsx-runtime";import*as r from"react";import{Button as o}from"../button/Button";export const IconButton=r.forwardRef(function({icon:r,label:e,style:a,...n},s){return t(o,{ref:s,"aria-label":e,startIcon:r,style:{borderRadius:"9999px",...a},...n})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{IconButton}from"./IconButton";
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* What may be typed into a slot.
|
|
5
|
+
*
|
|
6
|
+
* `numeric` is the default because that is what a texted code is, and it is also
|
|
7
|
+
* what puts a number pad in front of a phone. `any` accepts whatever the
|
|
8
|
+
* keyboard produces — for a licence key with punctuation in it.
|
|
9
|
+
*/
|
|
10
|
+
export type OtpFieldCharset = 'numeric' | 'alpha' | 'alphanumeric' | 'any';
|
|
11
|
+
export interface OtpFieldProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'defaultValue' | 'onChange' | 'children'> {
|
|
12
|
+
/**
|
|
13
|
+
* Drop shadow depth. `0` (the default) is flat.
|
|
14
|
+
* @default 0
|
|
15
|
+
*/
|
|
16
|
+
elevation?: NebaElevation;
|
|
17
|
+
/**
|
|
18
|
+
* How many characters the code has. Clamped to 2–12: a single box is a
|
|
19
|
+
* TextField, and past twelve the row stops fitting a phone.
|
|
20
|
+
* @default 6
|
|
21
|
+
*/
|
|
22
|
+
length?: number;
|
|
23
|
+
/**
|
|
24
|
+
* What may be typed. Anything rejected is dropped rather than shown, and
|
|
25
|
+
* `onValueInvalid` reports it.
|
|
26
|
+
* @default 'numeric'
|
|
27
|
+
*/
|
|
28
|
+
charset?: OtpFieldCharset;
|
|
29
|
+
/** Hides the characters, the way a password field does. @default false */
|
|
30
|
+
mask?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Splits the row every `groupSize` slots with a separator. `3` on a six digit
|
|
33
|
+
* code gives the familiar two blocks of three.
|
|
34
|
+
*/
|
|
35
|
+
groupSize?: number;
|
|
36
|
+
/** What is drawn between two groups. @default '–' */
|
|
37
|
+
separator?: React.ReactNode;
|
|
38
|
+
/** The code. Use with `onValueChange` for a controlled field. */
|
|
39
|
+
value?: string;
|
|
40
|
+
/** What it starts as, for an uncontrolled one. */
|
|
41
|
+
defaultValue?: string;
|
|
42
|
+
onValueChange?: (value: string) => void;
|
|
43
|
+
/** Fires once every slot is filled — the moment to verify the code. */
|
|
44
|
+
onComplete?: (value: string) => void;
|
|
45
|
+
/** Fires when typed or pasted text held characters the charset rejects. */
|
|
46
|
+
onValueInvalid?: (value: string) => void;
|
|
47
|
+
/** Submits the owning form as soon as the code is complete. @default false */
|
|
48
|
+
autoSubmit?: boolean;
|
|
49
|
+
/** Label above the row, wired to the slots by Base UI's Field. */
|
|
50
|
+
label?: React.ReactNode;
|
|
51
|
+
/** Helper text below the row. */
|
|
52
|
+
description?: React.ReactNode;
|
|
53
|
+
/** Error message below the row. Its presence also turns the field invalid. */
|
|
54
|
+
error?: React.ReactNode;
|
|
55
|
+
/**
|
|
56
|
+
* Forces the invalid state without a message — for when a form library owns
|
|
57
|
+
* the validity. Defaults to whether `error` has content.
|
|
58
|
+
*/
|
|
59
|
+
invalid?: boolean;
|
|
60
|
+
/** Identifies the field when a form is submitted. */
|
|
61
|
+
name?: string;
|
|
62
|
+
/** The form must have a complete code before it submits. @default false */
|
|
63
|
+
required?: boolean;
|
|
64
|
+
/** Unavailable. Every slot stops answering. */
|
|
65
|
+
disabled?: boolean;
|
|
66
|
+
/** Readable and copyable, but not typeable. @default false */
|
|
67
|
+
readOnly?: boolean;
|
|
68
|
+
/** Puts the caret in the first slot on mount. @default false */
|
|
69
|
+
autoFocus?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A row of one-character slots: a PIN, a texted verification code, an invite
|
|
73
|
+
* key.
|
|
74
|
+
*
|
|
75
|
+
* Base UI owns everything that makes this harder than it looks — one hidden
|
|
76
|
+
* value behind however many inputs, paste spread across the slots from wherever
|
|
77
|
+
* the caret was, backspace stepping back a box, a click landing on the first
|
|
78
|
+
* empty slot rather than the one under the pointer, and the autofill hook that
|
|
79
|
+
* lets a phone offer the code straight from the message.
|
|
80
|
+
*
|
|
81
|
+
* What is here is the ladder a slot is drawn on and the field shell it shares
|
|
82
|
+
* with TextField and Select — a slot is a field-shaped box, and a form holding
|
|
83
|
+
* both should not look like two form kits stacked on each other.
|
|
84
|
+
*/
|
|
85
|
+
export declare const OtpField: React.ForwardRefExoticComponent<OtpFieldProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as t from"react";import{Field as n}from"@base-ui/react/field";import{OTPField as l}from"@base-ui/react/otp-field";import{cx as r,disabledClasses as o,fieldReadOnlyClasses as i,fieldRestClasses as s,metaTextClasses as m,stackGapClasses as d,surfaceSlots as u,transitionClasses as c}from"../../internal/styles";const p={xs:"h-7 w-6",sm:"h-8 w-7",md:"h-10 w-8",lg:"h-12 w-10",xl:"h-14 w-12"},f={xs:"rounded-[0.3125rem]",sm:"rounded-[0.375rem]",md:"rounded-[0.4375rem]",lg:"rounded-[0.5625rem]",xl:"rounded-[0.6875rem]"},x={xs:"text-[0.8125rem]",sm:"text-[0.9375rem]",md:"text-[1.0625rem]",lg:"text-[1.25rem]",xl:"text-[1.5rem]"},g={default:{xs:"gap-1",sm:"gap-1",md:"gap-1.5",lg:"gap-2",xl:"gap-2.5"},compact:{xs:"gap-0.5",sm:"gap-0.5",md:"gap-1",lg:"gap-1",xl:"gap-1.5"}},h={numeric:"numeric",alpha:"alpha",alphanumeric:"alphanumeric",any:"none"};export const OtpField=t.forwardRef(function({variant:b="outline",size:y="md",color:v="primary",density:N="default",elevation:V=0,length:w=6,charset:C="numeric",mask:F=!1,groupSize:M,separator:j="–",value:I,defaultValue:O,onValueChange:R,onComplete:S,onValueInvalid:k,autoSubmit:q=!1,label:z,description:_,error:A,invalid:D,name:E,required:L=!1,disabled:T=!1,readOnly:B=!1,autoFocus:G=!1,className:H,style:J,...K},P){const Q=Math.min(12,Math.max(2,Math.round(w))),U=null!=A&&!1!==A&&""!==A,W=D??U,X=W?"danger":v,Y=r("text-center font-medium [font-family:inherit]",p[y],x[y],f[y],c,"caret-(--n-accent) selection:bg-(--n-soft-press)","focus:[outline:2px_solid_var(--n-ring)] focus:outline-offset-1","focus:[transition-duration:0ms]",T?o[b]:B?i[b]:s[b]),Z=M&&M>0?Math.round(M):0;return a(n.Root,{disabled:T,invalid:W,className:r("inline-flex flex-col align-top",d[y],H),style:{...u(X,V),...J},children:[z?e(n.Label,{className:r(m[y],"font-medium",T?"text-(--neba-disabled-fg)":"text-(--neba-fg)"),children:z}):null,e(l.Root,{ref:P,length:Q,validationType:h[C],mask:F,name:E,required:L,disabled:T,readOnly:B,autoSubmit:q,value:I,defaultValue:O,onValueChange:e=>R?.(e),onValueComplete:e=>S?.(e),onValueInvalid:e=>k?.(e),className:r("flex items-center",g[N][y]),...K,children:Array.from({length:Q},(n,o)=>a(t.Fragment,{children:[Z>0&&o>0&&o%Z===0?e("span",{"aria-hidden":"true",className:r("select-none px-0.5 text-(--neba-muted-fg)",x[y]),children:j}):null,e(l.Input,{className:Y,autoFocus:G&&0===o})]},o))}),_?e(n.Description,{className:r(m[y],"text-(--neba-muted-fg)"),children:_}):null,U?e(n.Error,{match:!0,className:r(m[y],"text-(--n-accent)"),children:A}):null]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{OtpField}from"./OtpField";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as a from"react";import{Button as n}from"../button/Button";import{ChevronIcon as
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as a from"react";import{Button as n}from"../button/Button";import{ChevronIcon as r}from"../../internal/icons";import{controlTextClasses as i,gapClasses as l,srOnlyClasses as o}from"../../internal/styles";function s(e,t){return Array.from({length:Math.max(0,t-e+1)},(t,a)=>e+a)}function m(){return e("svg",{viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:e("path",{d:"m7.5 4.5 3.5 3.5-3.5 3.5M3.5 4.5 7 8l-3.5 3.5",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}const c={xs:"h-5.5 min-w-5.5",sm:"h-6.5 min-w-6.5",md:"h-8 min-w-8",lg:"h-10 min-w-10",xl:"h-12 min-w-12"};export const Pagination=a.forwardRef(function({variant:d="text",size:u="md",color:h="primary",density:f="compact",elevation:p=0,count:x,page:g,defaultPage:b=1,onPageChange:v,siblingCount:M=1,boundaryCount:w=1,showEdges:N=!1,showArrows:y=!0,disabled:L=!1,label:j="Pagination",pageLabel:k=e=>`Page ${e}`,previousLabel:C="Previous page",nextLabel:P="Next page",firstLabel:$="First page",lastLabel:z="Last page",className:B,children:A,...E},F){const[I,R]=a.useState(b),S=Math.min(Math.max(g??I,1),Math.max(x,1)),W=e=>{const t=Math.min(Math.max(e,1),x);t!==S&&(void 0===g&&R(t),v?.(t))};if(x<2)return null;const q=function(e,t,a,n){const r=s(1,Math.min(n,e)),i=s(Math.max(e-n+1,n+1),e),l=Math.max(Math.min(t-a,e-n-2*a-1),n+2),o=Math.min(Math.max(t+a,n+2*a+2),i.length>0?i[0]-2:e-1);return[...r,...l>n+2?["start-ellipsis"]:n+1<e-n?[n+1]:[],...s(l,o),...o<e-n-1?["end-ellipsis"]:e-n>n?[e-n]:[],...i]}(x,S,M,w),D=S<=1,G=S>=x,H=(t,a,r,i,l,o)=>e("li",{className:"flex",children:e(n,{variant:d,size:u,color:h,density:f,elevation:p,disabled:L||i,"aria-label":a,startIcon:e("span",{className:`flex items-center ${l}`,children:o}),onClick:()=>W(r)})},t);return t("nav",{ref:F,"aria-label":j,className:["flex items-center",B??""].filter(Boolean).join(" "),...E,children:[t("ul",{role:"list",className:`m-0 flex list-none items-center p-0 ${l[u]}`,children:[N?H("first",$,1,D,"rotate-180 rtl:rotate-0",e(m,{})):null,y?H("previous",C,S-1,D,"rotate-90 rtl:-rotate-90",e(r,{})):null,q.map((t,a)=>e("li","number"==typeof t?{className:"flex",children:e(n,{variant:t===S?"solid":d,size:u,color:h,density:f,elevation:p,disabled:L,"aria-label":k(t),"aria-current":t===S?"page":void 0,className:"tabular-nums",onClick:()=>W(t),children:t})}:{"aria-hidden":"true",className:["flex select-none items-center justify-center","text-(--neba-muted-fg)",i[u],c[u]].join(" "),children:"…"},`slot-${a}`)),y?H("next",P,S+1,G,"-rotate-90 rtl:rotate-90",e(r,{})):null,N?H("last",z,x,G,"rtl:rotate-180",e(m,{})):null]}),e("span",{className:o,"aria-live":"polite",children:`${k(S)} of ${x}`}),A]})});
|
|
@@ -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,80 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaPosition, NebaStyleProps } from '../../types';
|
|
3
|
+
export interface PillProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'onClick' | 'title'> {
|
|
4
|
+
/**
|
|
5
|
+
* Drop shadow depth. `2` here, against the `0` everything else defaults to.
|
|
6
|
+
*
|
|
7
|
+
* That is not an inconsistency: a Pill is defined by not being part of the
|
|
8
|
+
* page. Every other surface in the library rests on the page and earns its
|
|
9
|
+
* separation from the acrylic edge, so a shadow is opt-in. This one hovers
|
|
10
|
+
* over whatever is underneath it, and a lozenge floating flat on the content
|
|
11
|
+
* it is floating over reads as a mistake.
|
|
12
|
+
* @default 2
|
|
13
|
+
*/
|
|
14
|
+
elevation?: NebaElevation;
|
|
15
|
+
/**
|
|
16
|
+
* The leading slot — a glyph, an avatar, a status dot, a photo.
|
|
17
|
+
*
|
|
18
|
+
* It is given a square box of its own and clipped to a circle, so an `<img>`
|
|
19
|
+
* lands in it as readily as an icon does: the image fills the box and is
|
|
20
|
+
* cropped rather than letterboxed, which is what a 20px avatar wants.
|
|
21
|
+
*/
|
|
22
|
+
startIcon?: React.ReactNode;
|
|
23
|
+
/** The trailing slot. Outside the pressable area, so it can be a control. */
|
|
24
|
+
endIcon?: React.ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* The headline in the middle — what the pill is currently about.
|
|
27
|
+
*
|
|
28
|
+
* A prop rather than something to compose, for the reason Card's title is one:
|
|
29
|
+
* the arrangement is fixed and what a caller wants to decide is what goes in
|
|
30
|
+
* each slot. Almost every pill is a line of text and an optional second line
|
|
31
|
+
* under it, and spelling that as children means every caller inventing their
|
|
32
|
+
* own centring and type scale.
|
|
33
|
+
*/
|
|
34
|
+
title?: React.ReactNode;
|
|
35
|
+
/** The second line, under the title. One step down and lighter. */
|
|
36
|
+
description?: React.ReactNode;
|
|
37
|
+
/**
|
|
38
|
+
* The second half, revealed when `expanded`.
|
|
39
|
+
*
|
|
40
|
+
* The pill grows downward into it rather than swapping to a different shape:
|
|
41
|
+
* one object saying more, which is the whole idea being borrowed here.
|
|
42
|
+
*/
|
|
43
|
+
details?: React.ReactNode;
|
|
44
|
+
/** Whether `details` is showing. @default false */
|
|
45
|
+
expanded?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* How it sits in the page's scroll. `fixed` pins it against the viewport and
|
|
48
|
+
* centres it horizontally, which is the arrangement this shape exists for.
|
|
49
|
+
* @default 'static'
|
|
50
|
+
*/
|
|
51
|
+
position?: NebaPosition;
|
|
52
|
+
/** Which edge it is held against when `position` is not `static`. @default 'top' */
|
|
53
|
+
side?: 'top' | 'bottom';
|
|
54
|
+
/** Passing it makes the row a real button. */
|
|
55
|
+
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
|
56
|
+
/**
|
|
57
|
+
* Anything the middle needs that `title` and `description` cannot say — a
|
|
58
|
+
* marquee, a pair of small readouts, a live counter. Rendered under them, in
|
|
59
|
+
* the same centred column.
|
|
60
|
+
*/
|
|
61
|
+
children?: React.ReactNode;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A floating lozenge holding a small amount of live information.
|
|
65
|
+
*
|
|
66
|
+
* The shape is a stadium, which the house radius rule otherwise forbids: every
|
|
67
|
+
* control is held just short of the 50% that would make it a pill, because the
|
|
68
|
+
* flat run along its top and bottom edge is what still reads as a sheet with the
|
|
69
|
+
* corners cut off it. A Pill is the exception the rule is drawn against, and the
|
|
70
|
+
* exception works for the same reason the rule does — this is not a sheet lying
|
|
71
|
+
* on the page. It is an object hovering over it, and an object hovering over the
|
|
72
|
+
* page should not look like it was cut from the same material.
|
|
73
|
+
*
|
|
74
|
+
* `details` is revealed by animating a measured height, exactly as an Accordion
|
|
75
|
+
* panel is: nothing is transformed and no text is resampled, the pill is simply
|
|
76
|
+
* a window that opens. The measurement is a `ResizeObserver` rather than a
|
|
77
|
+
* hardcoded height, so a details area whose content changes — which is what live
|
|
78
|
+
* information does — grows with it.
|
|
79
|
+
*/
|
|
80
|
+
export declare const Pill: React.ForwardRefExoticComponent<PillProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as n,Fragment as t}from"react/jsx-runtime";import*as o from"react";import{controlSlots as l,controlTextClasses as r,focusRingClasses as i,gapClasses as s,hasContent as a,iconClasses as c,iconSizeClasses as d,metaTextClasses as m,paddingXClasses as x,pressTransitionClasses as f,sheetBodyClasses as p,surfaceClasses as u,transitionClasses as h}from"../../internal/styles";const v={solid:[u,"text-(--n-on-solid) bg-(--n-fill)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[u,"border text-(--n-accent) bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:`${u} text-(--n-accent) bg-(--n-soft)`},b={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)"},g={xs:"min-h-5.5",sm:"min-h-6.5",md:"min-h-8",lg:"min-h-10",xl:"min-h-12"},w={xs:"rounded-[0.6875rem]",sm:"rounded-[0.8125rem]",md:"rounded-[1rem]",lg:"rounded-[1.25rem]",xl:"rounded-[1.5rem]"},j={default:{xs:"px-3",sm:"px-4",md:"px-5",lg:"px-6",xl:"px-8"},compact:{xs:"px-1.5",sm:"px-2",md:"px-2.5",lg:"px-3",xl:"px-4"}},y={static:{top:"",bottom:""},sticky:{top:"sticky top-3 z-20",bottom:"sticky bottom-3 z-20"},fixed:{top:"fixed inset-x-0 top-3 z-30 mx-auto w-fit",bottom:"fixed inset-x-0 bottom-3 z-30 mx-auto w-fit"}};export const Pill=o.forwardRef(function({variant:u="solid",size:N="md",color:k="secondary",density:z="default",elevation:$=2,startIcon:_,endIcon:C,title:R,description:B,details:H,expanded:I=!1,position:E="static",side:F="top",className:O,style:P,children:S,onClick:q,...A},D){const G=o.useRef(null),[J,K]=o.useState(0);o.useEffect(()=>{const e=G.current;if(!e)return;const n=new ResizeObserver(()=>{K(e.scrollHeight)});return n.observe(e),K(e.scrollHeight),()=>n.disconnect()},[H]);const L=Boolean(q),M=x[z][N],Q=n(t,{children:[a(_)?e("span",{className:`flex shrink-0 items-center justify-center overflow-hidden rounded-full [&_img]:size-full [&_img]:object-cover ${d[N]}`,children:_}):null,a(R)||a(B)||a(S)?n("span",{className:["flex min-w-0 flex-1 flex-col items-center justify-center text-center",j[z][N]].join(" "),children:[a(R)?e("span",{className:"max-w-full truncate",children:R}):null,a(B)?e("span",{className:`max-w-full truncate font-normal ${m[N]} [color:color-mix(in_oklab,currentColor_72%,transparent)]`,children:B}):null,S]}):null]});return n("div",{ref:D,className:["inline-flex max-w-full flex-col overflow-hidden align-middle",w[N],"font-medium whitespace-nowrap select-none",r[N],v[u],h,f,c,L?`neba-glow ${b[u]}`:"",y[E][F],O??""].filter(Boolean).join(" "),style:{...l(k,$,u),...P},...A,children:[n("div",{className:["flex shrink-0 items-center py-1",g[N],s[N],L?"ps-0":M,a(C)?"pe-1":L?"pe-0":""].join(" "),children:[L?e("button",{type:"button",className:["flex min-w-0 flex-1 cursor-pointer items-center justify-center self-stretch","rounded-[inherit]",s[N],M,i].join(" "),onClick:q,children:Q}):Q,a(C)?e("span",{className:"flex h-[1lh] shrink-0 items-center",children:C}):null]}),a(H)?e("div",{className:["overflow-hidden","[transition:height_var(--neba-duration)_var(--neba-ease)]","motion-reduce:[transition-duration:0ms]"].join(" "),style:{height:I?J:0},inert:!I,children:e("div",{ref:G,className:`whitespace-normal ${M} pb-2 ${p[N]}`,children:H})}):null]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Pill}from"./Pill";
|