@sesamehr/react-design-system 2.0.0-beta.6 → 2.0.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/Button/ActionItem/ActionItem.d.ts +40 -0
  2. package/dist/Button/ActionItem/index.d.ts +1 -0
  3. package/dist/Data/Table/TableScrollbar/TableScrollbar.d.ts +5 -3
  4. package/dist/Display/Avatar/AvatarSkeleton/AvatarSkeleton.d.ts +30 -0
  5. package/dist/Display/Avatar/index.d.ts +2 -0
  6. package/dist/Display/ChatBubble/ChatBubbleStatus/ChatBubbleStatus.d.ts +2 -2
  7. package/dist/Display/FileCard/FileCard.d.ts +66 -0
  8. package/dist/Display/FileCard/index.d.ts +1 -0
  9. package/dist/Display/FilePreviewCard/FilePreviewCard.d.ts +49 -0
  10. package/dist/Display/FilePreviewCard/index.d.ts +1 -0
  11. package/dist/Feedback/Alert/AlertIcon.d.ts +1 -1
  12. package/dist/Feedback/Dialog/DialogBody.d.ts +18 -0
  13. package/dist/Feedback/Dialog/index.d.ts +1 -0
  14. package/dist/Feedback/Popover/PopoverContent.d.ts +7 -1
  15. package/dist/Feedback/Progress/index.d.ts +1 -1
  16. package/dist/Feedback/Skeleton/Skeleton.d.ts +20 -0
  17. package/dist/Feedback/Skeleton/index.d.ts +2 -0
  18. package/dist/Forms/Field/FieldGroup/FieldGroup.d.ts +16 -1
  19. package/dist/Forms/Field/FieldLabel/FieldLabel.d.ts +16 -0
  20. package/dist/Forms/Field/FieldMessage/FieldMessage.d.ts +22 -2
  21. package/dist/Forms/Field/index.d.ts +1 -0
  22. package/dist/Forms/Field/useFieldGroup.d.ts +108 -0
  23. package/dist/Forms/Inputs/Calendar/Calendar.d.ts +2 -7
  24. package/dist/Forms/Inputs/Calendar/CalendarRange.d.ts +2 -7
  25. package/dist/Forms/Inputs/Calendar/date.d.ts +28 -0
  26. package/dist/Forms/Inputs/Calendar/index.d.ts +1 -0
  27. package/dist/Forms/Inputs/Calendar/nav.d.ts +34 -0
  28. package/dist/Forms/Inputs/Calendar/testids.d.ts +9 -6
  29. package/dist/Forms/Inputs/Calendar/useCalendarLocale.d.ts +18 -0
  30. package/dist/Forms/Inputs/ChatInput/ChatInput.d.ts +68 -0
  31. package/dist/Forms/Inputs/ChatInput/index.d.ts +46 -0
  32. package/dist/Forms/Inputs/Combobox/ComboboxContent.d.ts +5 -0
  33. package/dist/Forms/Inputs/Combobox/ComboboxGroup.d.ts +6 -2
  34. package/dist/Forms/Inputs/Combobox/ComboboxInput.d.ts +6 -1
  35. package/dist/Forms/Inputs/Combobox/ComboboxItem.d.ts +4 -0
  36. package/dist/Forms/Inputs/Combobox/context.d.ts +7 -0
  37. package/dist/Forms/Inputs/Combobox/useComboboxLabels.d.ts +21 -0
  38. package/dist/Forms/Inputs/DatePicker/DatePicker.d.ts +51 -0
  39. package/dist/Forms/Inputs/DatePicker/DatePickerHeader.d.ts +65 -0
  40. package/dist/Forms/Inputs/DatePicker/DatePickerMonthSelect.d.ts +27 -0
  41. package/dist/Forms/Inputs/DatePicker/DatePickerTrigger.d.ts +29 -0
  42. package/dist/Forms/Inputs/DatePicker/DatePickerYearSelect.d.ts +25 -0
  43. package/dist/Forms/Inputs/DatePicker/DateRangePicker.d.ts +61 -0
  44. package/dist/Forms/Inputs/DatePicker/format.d.ts +44 -0
  45. package/dist/Forms/Inputs/DatePicker/index.d.ts +6 -0
  46. package/dist/Forms/Inputs/DatePicker/month.d.ts +28 -0
  47. package/dist/Forms/Inputs/DatePicker/useDatePickerHeader.d.ts +23 -0
  48. package/dist/Forms/Inputs/InputOtp/InputOtp.d.ts +7 -0
  49. package/dist/Forms/Inputs/InputPassword/InputPassword.d.ts +9 -1
  50. package/dist/Forms/Inputs/InputText/InputText.d.ts +9 -0
  51. package/dist/Forms/Inputs/SearchBar/useSearchDelay.d.ts +1 -1
  52. package/dist/Forms/Inputs/SearchPanel/SearchPanel.d.ts +71 -0
  53. package/dist/Forms/Inputs/SearchPanel/SearchPanelEmpty.d.ts +16 -0
  54. package/dist/Forms/Inputs/SearchPanel/SearchPanelGroup.d.ts +14 -0
  55. package/dist/Forms/Inputs/SearchPanel/SearchPanelInput.d.ts +31 -0
  56. package/dist/Forms/Inputs/SearchPanel/SearchPanelItem.d.ts +20 -0
  57. package/dist/Forms/Inputs/SearchPanel/SearchPanelList.d.ts +42 -0
  58. package/dist/Forms/Inputs/SearchPanel/context.d.ts +11 -0
  59. package/dist/Forms/Inputs/SearchPanel/index.d.ts +6 -0
  60. package/dist/Forms/Inputs/Select/SelectContent.d.ts +6 -0
  61. package/dist/Forms/Inputs/Select/SelectGroup.d.ts +6 -2
  62. package/dist/Forms/Inputs/Select/SelectItem.d.ts +6 -0
  63. package/dist/Forms/Inputs/Textarea/Textarea.d.ts +69 -0
  64. package/dist/Forms/Inputs/Textarea/index.d.ts +21 -0
  65. package/dist/Forms/Inputs/Toggle/RadioButton/RadioGroup.d.ts +5 -0
  66. package/dist/Forms/Inputs/Toggle/ToggleLabel/ToggleLabel.d.ts +10 -1
  67. package/dist/Forms/Inputs/index.d.ts +4 -0
  68. package/dist/Layout/Scrollbar/Scrollbar.d.ts +46 -0
  69. package/dist/Layout/Scrollbar/index.d.ts +2 -0
  70. package/dist/Layout/Scrollbar/useScrollbar.d.ts +52 -0
  71. package/dist/hooks/index.d.ts +12 -0
  72. package/dist/hooks/useAutoGrow.d.ts +22 -0
  73. package/dist/internal/Listbox/ListboxEmpty.d.ts +3 -0
  74. package/dist/internal/Listbox/ListboxItem.d.ts +23 -0
  75. package/dist/internal/Listbox/ListboxLabel.d.ts +9 -0
  76. package/dist/internal/Listbox/ListboxSeparator.d.ts +3 -0
  77. package/dist/internal/Listbox/ListboxViewport.d.ts +9 -0
  78. package/dist/internal/Surface/Surface.d.ts +35 -0
  79. package/dist/internal/index.d.ts +13 -0
  80. package/dist/main.d.ts +10 -4
  81. package/dist/react-design-system.css +1 -1
  82. package/dist/react-design-system.js +10563 -9466
  83. package/dist/react-design-system.umd.cjs +156 -132
  84. package/package.json +1 -2
@@ -15,6 +15,14 @@ export interface InputPasswordProps extends Omit<React.HTMLAttributes<HTMLDivEle
15
15
  * InputPassword root — mirrors `@sesame/orxata-core` InputPassword.
16
16
  * Provides the value and clear-text state to `InputPasswordInput` and
17
17
  * `InputPasswordToggle` through context.
18
- * Both `field` and `visible` work controlled or uncontrolled.
18
+ *
19
+ * Both `field` and `visible` work controlled or uncontrolled, and which one
20
+ * you get depends on **whether the change handler is there, not whether the
21
+ * value is**. The Vue original is a pair of `defineModel`s, and a
22
+ * `defineModel` given a value but no listener keeps driving itself. Gate on
23
+ * the value alone and `visible={false}` — a perfectly ordinary way to say
24
+ * "start hidden" — turns the reveal toggle into a dead button: `toggle()`
25
+ * computes the next state, declines to store it because the prop is present,
26
+ * and calls a handler nobody passed.
19
27
  */
20
28
  export declare const InputPassword: import('react').ForwardRefExoticComponent<InputPasswordProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -12,5 +12,14 @@ export interface InputTextProps extends Omit<React.InputHTMLAttributes<HTMLInput
12
12
  * `field` + `onFieldChange` are the controlled pair; native `value`/`onChange`
13
13
  * still pass through via `...props` for anyone who prefers them. `type`
14
14
  * defaults to `'text'`, and a `type` passed by the caller wins.
15
+ *
16
+ * **`field` alone does not mean "controlled" — `field` *with* `onFieldChange`
17
+ * does.** The Vue original is a `defineModel`, and a `defineModel` only hands
18
+ * control to the parent when the parent actually listens; given a value and no
19
+ * listener it seeds its own state and goes on working. Port `field` as
20
+ * unconditionally controlled and that same usage becomes an input pinned to
21
+ * its initial value with nothing able to change it — one that silently will
22
+ * not accept a keystroke. Which is exactly what `<OxInputText field="" />`
23
+ * did, in the story and in any app that wrote it.
15
24
  */
16
25
  export declare const InputText: import('react').ForwardRefExoticComponent<InputTextProps & import('react').RefAttributes<HTMLInputElement>>;
@@ -2,7 +2,7 @@
2
2
  * The quiet period between typing and searching — mirrors
3
3
  * `@sesame/orxata-core` `useSearchDelay`.
4
4
  *
5
- * A search field that emits on every keystroke turns "María" into five
5
+ * A search field that emits on every keystroke turns "Marie" into five
6
6
  * requests, four of which are already stale by the time they land. Waiting for
7
7
  * typing to stop collapses them into one.
8
8
  *
@@ -0,0 +1,71 @@
1
+ import { Command } from 'cmdk';
2
+ export interface SearchPanelProps extends Omit<React.ComponentPropsWithoutRef<typeof Command>, 'value' | 'onValueChange' | 'onSelect'> {
3
+ /** A row was chosen — by click or by Enter. Carries that row's `value`. */
4
+ onSelect?: (value: string) => void;
5
+ /** Escape was pressed. What closing means is the caller's to decide. */
6
+ onClose?: () => void;
7
+ dataTestid: string;
8
+ }
9
+ /**
10
+ * A panel of search results, grouped under headings — the command-palette
11
+ * pattern. Choosing a row runs something: it navigates, opens a document, or
12
+ * fires an action.
13
+ *
14
+ * Named for what it is rather than what it shows. It was `SearchResults`, which
15
+ * described the one thing it does not do: it does not search, does not filter
16
+ * and produces no results — the caller supplies the rows. What it *is* is the
17
+ * surface searching happens on, a field above a grouped list, and `SearchPanel`
18
+ * promises nothing it cannot keep.
19
+ *
20
+ * `Menu` was the other candidate and is wrong for a reason worth writing down:
21
+ * in ARIA a `menu` is application commands with their own keyboard model, and
22
+ * this is a `listbox`. A component called `Menu` that renders `role="listbox"`
23
+ * invites the next reader to resolve the mismatch in the wrong direction.
24
+ *
25
+ * **This card is the field, and nothing else.** Collapsed it is exactly the
26
+ * search row with the same air above it as below; the results are not in it.
27
+ * `SearchPanelList` draws its own card and floats it over the page, tethered
28
+ * to this one — so a result arriving moves nothing below the panel, which is
29
+ * the whole reason a global search can live in a header. See that file for how
30
+ * the two are made to read as one surface.
31
+ *
32
+ * `Popover.Root` is pinned open and holds no state: the caller decides whether
33
+ * the list renders at all, so there is no second open state here. What it is
34
+ * for is the anchor — the card below is the box the list positions itself
35
+ * against, flips away from and takes its width from.
36
+ *
37
+ * **The list brings its own motion**, because the popper gives it a
38
+ * `data-state` and the shared surface animates on that. Do not wrap it in an
39
+ * `OxTransition*`: those animate a box growing in the flow, which is the
40
+ * behaviour this component was changed to stop, and their enter class would be
41
+ * competing with the surface's own keyframe for the `animation` property.
42
+ *
43
+ * Deliberately not a `Combobox`. A combobox's model is "pick a value, the
44
+ * field then shows it", which is wrong here twice over — the search box must
45
+ * keep the words the user typed, and a row that ran an action is not a value
46
+ * anything holds afterwards. Bending a combobox to this shape means clearing
47
+ * the value after every pick and suppressing the selected state, and the bug
48
+ * that arrives three months later is the one where a stale selection survives
49
+ * a reset. shadcn keeps `Command` apart from `Combobox` for the same reason.
50
+ *
51
+ * So this root reports `onSelect` and holds nothing. cmdk's own `value` is the
52
+ * *highlight*, not a selection, which is exactly the model wanted: no row is
53
+ * ever checked and no row draws a check.
54
+ *
55
+ * `shouldFilter` is off. The caller already has the term — `OxSearchBar` owns
56
+ * the debounce — and whether a result matches is a question about their data,
57
+ * not about the strings in the DOM.
58
+ *
59
+ * The component knows nothing about what is in it. The headings, the icons and
60
+ * the row contents are all `children`: a caller listing employees with avatars
61
+ * and files with sizes needs nothing here to change. The categories in the
62
+ * stories are one application's, not this package's.
63
+ *
64
+ * The keyboard comes from cmdk: arrows and Home/End move the highlight while
65
+ * focus stays in the search field, Enter runs the highlighted row, and
66
+ * `aria-activedescendant` keeps a screen reader in step. Focusing the field
67
+ * highlights the first row, so Enter on a fresh panel runs the top hit without
68
+ * arrowing to it — which also means the first arrow press moves to the
69
+ * *second* row. Escape is cmdk's one gap and is handled here.
70
+ */
71
+ export declare const SearchPanel: import('react').ForwardRefExoticComponent<SearchPanelProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,16 @@
1
+ export type SearchPanelEmptyProps = React.ComponentPropsWithoutRef<'div'>;
2
+ /**
3
+ * Shown in place of the rows when a search found nothing.
4
+ *
5
+ * No default text. "No results found" is English, and which of the caller's
6
+ * words belong here — and in which language — is not this package's to guess;
7
+ * `OxComboboxEmpty` carries one for backwards compatibility and that is the
8
+ * exception, not the pattern. Rendering is the caller's condition too: only
9
+ * they know whether an empty list means "nothing matched" or "nothing typed
10
+ * yet".
11
+ *
12
+ * Deliberately not cmdk's `CommandEmpty`, which decides for itself when to
13
+ * appear by counting the items its own filter left — and this panel's filter
14
+ * is the caller's.
15
+ */
16
+ export declare const SearchPanelEmpty: import('react').ForwardRefExoticComponent<Omit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,14 @@
1
+ import { CommandGroup } from 'cmdk';
2
+ export interface SearchPanelGroupProps extends React.ComponentPropsWithoutRef<typeof CommandGroup> {
3
+ /** The heading above the rows. Anything renderable, and always the caller's. */
4
+ label?: React.ReactNode;
5
+ }
6
+ /**
7
+ * A heading and the rows under it.
8
+ *
9
+ * Nothing here knows what the heading might say. "Documents", "Recent
10
+ * conversations", "People" are one application's categories on one day; a
11
+ * design system that shipped an enum of them would be wrong by the following
12
+ * release, and wrong for every other application immediately.
13
+ */
14
+ export declare const SearchPanelGroup: import('react').ForwardRefExoticComponent<SearchPanelGroupProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,31 @@
1
+ import { CommandInput } from 'cmdk';
2
+ export type SearchPanelInputProps = Omit<React.ComponentPropsWithoutRef<typeof CommandInput>, 'asChild'>;
3
+ /**
4
+ * Joins the caller's search field to the list's keyboard.
5
+ *
6
+ * Put exactly one control inside — `OxSearchBar` is the intended one, since it
7
+ * already owns the debounce. `asChild` hands it the arrow/Home/End/Enter
8
+ * handlers and `aria-activedescendant`, so the highlight moves through the
9
+ * rows while focus never leaves the field. Radix's `Slot`, which cmdk uses,
10
+ * throws on a second child rather than quietly merging onto the first.
11
+ *
12
+ * This renders no field of its own on purpose. A results panel is a way of
13
+ * showing what a search found, not a second opinion about what a search box
14
+ * looks like.
15
+ *
16
+ * It does render the row the field sits in, which is a different thing: the
17
+ * design draws a padded title row across the top of the panel, and `asChild`
18
+ * alone would have put that padding on the caller's search bar — where it would
19
+ * have moved the field's own border in from the panel edge instead of insetting
20
+ * the row.
21
+ *
22
+ * Two things cmdk decides that the field cannot override, because it writes
23
+ * them *after* the spread: the control becomes `type="text"` rather than
24
+ * `type="search"`, and it is given `role="combobox"` with `aria-controls` and
25
+ * `aria-activedescendant`. Neither is a problem — the only thing `type=search`
26
+ * adds is the browser's own clear button, which `OxSearchBar` hides and
27
+ * redraws anyway, and a text field that owns a list of options is what the
28
+ * combobox role is for, whatever the panel above it is called. It is a real
29
+ * difference from the Vue port, where the field keeps `type="search"`.
30
+ */
31
+ export declare const SearchPanelInput: import('react').ForwardRefExoticComponent<SearchPanelInputProps & import('react').RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,20 @@
1
+ import { CommandItem } from 'cmdk';
2
+ export interface SearchPanelItemProps extends Omit<React.ComponentPropsWithoutRef<typeof CommandItem>, 'value' | 'onSelect'> {
3
+ /** Handed back through the panel's `onSelect`. Unique within the panel. */
4
+ value: string;
5
+ disabled?: boolean;
6
+ }
7
+ /**
8
+ * One result. Choosing it makes the panel call `onSelect` with this `value`.
9
+ *
10
+ * No check indicator, and that is the point: nothing here is selected in the
11
+ * sense a `Select` option is. The row is a thing you run, so it never becomes
12
+ * the panel's current answer and never grows a tick beside it. The shared row
13
+ * only leaves room on the right when an indicator is passed, so these sit
14
+ * flush.
15
+ *
16
+ * Whatever goes inside is the caller's — an icon and a label, an avatar and
17
+ * two lines, a filename and a size. `value` is the only thing this package
18
+ * wants, and only so it has something to hand back.
19
+ */
20
+ export declare const SearchPanelItem: import('react').ForwardRefExoticComponent<SearchPanelItemProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,42 @@
1
+ import { CommandList } from 'cmdk';
2
+ export type SearchPanelListProps = React.ComponentPropsWithoutRef<typeof CommandList>;
3
+ /**
4
+ * The scrolling list of groups and rows — `role="listbox"` and the rows in it —
5
+ * on a card that floats over the page.
6
+ *
7
+ * **It floats, and that is the point.** In the flow it pushed everything below
8
+ * the panel down the moment an answer landed, which is not what a dropdown does
9
+ * and is disqualifying for a search that lives in a header. The card is
10
+ * positioned by Radix's popper against the anchor `SearchPanel` wraps around
11
+ * itself, so it is exactly the field's width, it flips above the field when
12
+ * there is no room below, and it is `position: fixed` — no ancestor's
13
+ * `overflow` can clip it.
14
+ *
15
+ * **No portal, and that is also the point.** Everything the keyboard is made of
16
+ * is a relationship between this list and the field above it: cmdk gathers the
17
+ * rows by querying the `Command` element they sit inside, `aria-activedescendant`
18
+ * on the field points at a row's `id`, and the panel's custom properties reach
19
+ * the rows by inheriting down the tree. A `Popover.Portal` would move the rows
20
+ * out of `Command` entirely, and the arrows would stop finding them.
21
+ * Popper-positioned content does not need a portal to escape the flow —
22
+ * `position: fixed` already does — so it does not get one.
23
+ *
24
+ * The two auto-focus events are prevented for the same reason. A popover
25
+ * focuses its content when it opens and hands focus back when it closes; both
26
+ * would take focus off the search field, which is the one thing the arrow keys
27
+ * depend on. `role="presentation"` and the blanked `aria-labelledby` finish the
28
+ * job: the primitive believes it is a dialog, a screen reader must not, and the
29
+ * name it points at belongs to a trigger this panel does not have. Radix
30
+ * spreads the caller's props *after* its own `role`, so saying so here is
31
+ * enough — the Vue port has to write the same two attributes one level further
32
+ * down, where reka merges last.
33
+ */
34
+ export declare const SearchPanelList: import('react').ForwardRefExoticComponent<Omit<{
35
+ children?: React.ReactNode;
36
+ } & Pick<Pick<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof import('react').HTMLAttributes<HTMLDivElement>> & {
37
+ ref?: React.Ref<HTMLDivElement>;
38
+ } & {
39
+ asChild?: boolean;
40
+ }, "asChild" | "key" | keyof import('react').HTMLAttributes<HTMLDivElement>> & {
41
+ label?: string;
42
+ } & import('react').RefAttributes<HTMLDivElement>, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,11 @@
1
+ export interface SearchPanelContextValue {
2
+ /** Called with a row's `value` when that row is chosen. */
3
+ select: (value: string) => void;
4
+ }
5
+ export declare const SearchPanelContext: import('react').Context<SearchPanelContextValue | null>;
6
+ /**
7
+ * The Vue port reaches the root through reka's own listbox context; React has
8
+ * no equivalent to inject, so the root publishes one. The error message
9
+ * matches the Vue side's on purpose.
10
+ */
11
+ export declare const useSearchPanelContext: () => SearchPanelContextValue;
@@ -0,0 +1,6 @@
1
+ export { SearchPanel, type SearchPanelProps } from './SearchPanel.tsx';
2
+ export { SearchPanelEmpty, type SearchPanelEmptyProps, } from './SearchPanelEmpty.tsx';
3
+ export { SearchPanelGroup, type SearchPanelGroupProps, } from './SearchPanelGroup.tsx';
4
+ export { SearchPanelInput, type SearchPanelInputProps, } from './SearchPanelInput.tsx';
5
+ export { SearchPanelItem, type SearchPanelItemProps, } from './SearchPanelItem.tsx';
6
+ export { SearchPanelList, type SearchPanelListProps, } from './SearchPanelList.tsx';
@@ -7,5 +7,11 @@ export interface SelectContentProps extends Omit<React.ComponentPropsWithoutRef<
7
7
  /**
8
8
  * The floating list. Always positioned against the trigger rather than over it,
9
9
  * so the popup matches the trigger's width and never covers it.
10
+ *
11
+ * `asChild` hands the primitive's positioning, `data-state` and `data-side` to
12
+ * the shared `Surface`, which is the one element `Popover`, `Select`,
13
+ * `Combobox` and `SearchPanel` may have in common. Exactly one child sits at
14
+ * each slot position — Radix's `Slot` throws on a second, which is the whole
15
+ * reason the arrangement is safe to read at a glance.
10
16
  */
11
17
  export declare const SelectContent: import('react').ForwardRefExoticComponent<SelectContentProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -1,7 +1,11 @@
1
1
  import * as SelectPrimitive from '@radix-ui/react-select';
2
2
  export interface SelectGroupProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Group> {
3
- /** Optional group heading. */
4
- label?: string;
3
+ /**
4
+ * The group heading. A node rather than a string: it is the caller's word
5
+ * for their own category, and taking it as text would decide for them that
6
+ * it can only ever be text.
7
+ */
8
+ label?: React.ReactNode;
5
9
  }
6
10
  /** Groups related options under a heading, announced as a group label. */
7
11
  export declare const SelectGroup: import('react').ForwardRefExoticComponent<SelectGroupProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -6,6 +6,12 @@ export interface SelectItemProps extends React.ComponentPropsWithoutRef<typeof S
6
6
  /**
7
7
  * A single option.
8
8
  *
9
+ * `asChild` hands Radix's item behaviour — `role="option"`, `data-highlighted`,
10
+ * `data-state`, typeahead, selection — to the shared row, which is the one
11
+ * thing `Select`, `Combobox` and `SearchPanel` may share. The primitive
12
+ * itself is not shared: this one and `Combobox`'s wrap different roots and
13
+ * swapping them breaks the keyboard in both.
14
+ *
9
15
  * The rendered text is published to the root on mount so the trigger's render
10
16
  * prop can name the selection. Reading it off the DOM keeps arbitrary markup
11
17
  * working, and it runs while the list is closed too: the primitive keeps items
@@ -0,0 +1,69 @@
1
+ import { ReactNode } from 'react';
2
+ import { TextareaCounterState } from './index.ts';
3
+ export interface TextareaProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'rows' | 'children'> {
4
+ /**
5
+ * How tall the surface may grow before it starts to scroll, counted in lines
6
+ * of text.
7
+ *
8
+ * In rows rather than pixels so the cap moves with the line height instead
9
+ * of cutting a row in half the first time anyone changes the type scale. The
10
+ * default is the design's 100px box, which is four 20px lines inside its own
11
+ * padding.
12
+ */
13
+ maxRows?: number;
14
+ /** Controlled value. Pair with `onFieldChange`; omit for uncontrolled. */
15
+ field?: string;
16
+ /** Called on every input with the next value. */
17
+ onFieldChange?: (value: string) => void;
18
+ /**
19
+ * Bottom-right inside the box. A node, or a render prop called with
20
+ * `{ value, length }` — the limit is the caller's business and the count is
21
+ * the value's, so neither is a prop.
22
+ */
23
+ counter?: ReactNode | ((state: TextareaCounterState) => ReactNode);
24
+ dataTestid: string;
25
+ }
26
+ /**
27
+ * The multi-line field: a `<textarea>` in the same box the single-line
28
+ * controls draw, that grows with its content and then scrolls.
29
+ * Mirrors `@sesame/orxata-core` Textarea.
30
+ *
31
+ * **Not `OxChatInput`.** That one is a chat composer — it owns a send
32
+ * contract, Enter submits, and it draws two corners of action buttons around
33
+ * the text. This one is a form field: it has no idea what happens to the
34
+ * value, Enter inserts a newline, and it sits inside an `<OxFieldGroup>` with
35
+ * a label above it and a message below. Reach for `OxChatInput` when the value
36
+ * is a message being sent somewhere; reach for this when the value is an
37
+ * answer on a form. They share `useAutoGrow` and nothing else, which is the
38
+ * only part of the two that is genuinely the same problem.
39
+ *
40
+ * **The label and the message are not here, and that is the point.**
41
+ * `<OxFieldLabel>` and `<OxFieldMessage>` already exist, already order
42
+ * themselves around a control, and — through `useFieldControl` below — already
43
+ * carry the `id`, `aria-invalid` and `aria-describedby` that tie the three
44
+ * together. A `label` prop would be a second, worse spelling of a component
45
+ * that is already in the package, and the design system ships no strings
46
+ * anyway.
47
+ *
48
+ * ```tsx
49
+ * <OxFieldGroup variant="error">
50
+ * <OxFieldLabel>Notes</OxFieldLabel>
51
+ * <OxTextarea field={notes} onFieldChange={setNotes} dataTestid="notes" />
52
+ * <OxFieldMessage>Keep it under 100 characters.</OxFieldMessage>
53
+ * </OxFieldGroup>
54
+ * ```
55
+ *
56
+ * **`field` alone does not mean "controlled" — `field` *with* `onFieldChange`
57
+ * does.** The Vue original is a `defineModel`, and a `defineModel` only hands
58
+ * control to the parent when the parent actually listens; given a value and no
59
+ * listener it seeds its own state and goes on working. Port `field` as
60
+ * unconditionally controlled and the same usage becomes a field pinned to its
61
+ * initial value that silently will not accept a keystroke.
62
+ *
63
+ * The `ref` is the **textarea**, not the box — which is where it differs from
64
+ * `OxChatInput`, whose ref is its root. This is a form control: what a caller
65
+ * reaches for a ref to do is focus it, select in it, or hand it to a form
66
+ * library. `className` still lands on the box, since the box is what reads as
67
+ * the control.
68
+ */
69
+ export declare const Textarea: import('react').ForwardRefExoticComponent<TextareaProps & import('react').RefAttributes<HTMLTextAreaElement>>;
@@ -0,0 +1,21 @@
1
+ export { Textarea, type TextareaProps } from './Textarea.tsx';
2
+ /**
3
+ * What the `counter` slot is handed.
4
+ *
5
+ * Both numbers come from the field rather than from the caller, so a counter
6
+ * works the same whether the value is controlled or left to the textarea — an
7
+ * uncontrolled field posting itself in a plain `<form>` has no value in the
8
+ * caller's scope to count.
9
+ *
10
+ * There is no `cva` here on purpose. `Textarea` has no variants: the design
11
+ * draws one box, and its states come from `OxFieldGroup` through the
12
+ * `--controller-*` properties rather than from a class this component picks.
13
+ * An empty variant map would be one more thing to keep in step across two
14
+ * packages in exchange for nothing.
15
+ */
16
+ export interface TextareaCounterState {
17
+ /** The text the field holds now. */
18
+ value: string;
19
+ /** How many characters that is — what `<OxCounter>` wants for its `value`. */
20
+ length: number;
21
+ }
@@ -5,5 +5,10 @@ export interface RadioGroupProps extends React.ComponentPropsWithoutRef<typeof R
5
5
  /**
6
6
  * RadioGroup — built on `@radix-ui/react-radio-group`.
7
7
  * `v-model` maps to Radix `value` / `onValueChange` (+ `defaultValue`).
8
+ *
9
+ * The group is what a field names, not the radios inside it: one id cannot sit
10
+ * on several `<button role="radio">`s at once, so `RadioButton` stays out of
11
+ * the field context and this root carries `aria-labelledby` instead. Radix
12
+ * gives it `role="radiogroup"`, which is what makes that name land.
8
13
  */
9
14
  export declare const RadioGroup: import('react').ForwardRefExoticComponent<RadioGroupProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -1,5 +1,14 @@
1
1
  export interface ToggleLabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
2
2
  htmlFor?: string;
3
3
  }
4
- /** ToggleLabel — mirrors `@sesame/orxata-core` ToggleLabel. */
4
+ /**
5
+ * ToggleLabel — mirrors `@sesame/orxata-core` ToggleLabel.
6
+ *
7
+ * `htmlFor` falls back to whichever control claimed the surrounding
8
+ * `<OxFieldGroup>`'s id, because the commonest checkbox layout in the system —
9
+ * a field wrapping an `<OxToggleGroup>` wrapping a toggle and this — would
10
+ * otherwise leave the toggle's own label naming nothing. It does not take the
11
+ * group's `labelId`: `OxFieldLabel` already carries that, and two elements
12
+ * cannot share an id.
13
+ */
5
14
  export declare const ToggleLabel: import('react').ForwardRefExoticComponent<ToggleLabelProps & import('react').RefAttributes<HTMLLabelElement>>;
@@ -5,6 +5,10 @@ export * from './InputPassword/index.ts';
5
5
  export * from './InputOtp/index.ts';
6
6
  export * from './Calendar/index.ts';
7
7
  export * from './Combobox/index.ts';
8
+ export * from './ChatInput/index.ts';
9
+ export * from './DatePicker/index.ts';
8
10
  export * from './SearchBar/index.ts';
11
+ export * from './SearchPanel/index.ts';
9
12
  export * from './Select/index.ts';
13
+ export * from './Textarea/index.ts';
10
14
  export * from './Toggle/index.ts';
@@ -0,0 +1,46 @@
1
+ import { ScrollbarPort } from './useScrollbar.ts';
2
+ export interface ScrollbarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
3
+ /**
4
+ * The element that scrolls. Everything is measured from it, and its native
5
+ * scrollbar is hidden for as long as this one is drawn over it.
6
+ */
7
+ port?: ScrollbarPort;
8
+ dataTestid: string;
9
+ }
10
+ /**
11
+ * An overlay scrollbar for a container that scrolls vertically, drawn instead
12
+ * of the browser's own. Mirrors `@sesame/orxata-core` Scrollbar.
13
+ *
14
+ * Reach for it where the native bar cannot say what it needs to say. A
15
+ * virtualised table is the case it was built for: the bar has to start below
16
+ * the column names, and the native one runs the full height of the scrollport
17
+ * because the scrollport contains the header. A chat composer that grows to a
18
+ * few lines and then scrolls is the other: a native bar there is a strip of
19
+ * system chrome inside a rounded box, and in the browsers that reserve a gutter
20
+ * it shifts the text sideways the moment the field overflows.
21
+ *
22
+ * It behaves the way an overlay bar does: out of the way until you scroll or
23
+ * reach for the edge, and gone again shortly after. The track takes no clicks —
24
+ * only the thumb does — so the strip down the right of the content stays live.
25
+ *
26
+ * **Placement is the parent's.** This positions itself absolutely inside the
27
+ * nearest positioned ancestor and reads four custom properties for where that
28
+ * is, all with fallbacks that spell "fill the containing block down its right
29
+ * edge": `--scrollbar-top` (`0px`), `--scrollbar-right` (`0px`),
30
+ * `--scrollbar-left` (`auto`) and `--scrollbar-height` (`100%`).
31
+ *
32
+ * A parent whose scrollport *is* the containing block sets none of them. One
33
+ * that has to compensate for something — a header inside the scrollport, an
34
+ * `offsetParent` that is not the scrollport — works the numbers out itself and
35
+ * writes them here, which is why none of that arithmetic is a prop.
36
+ *
37
+ * Vertical only, deliberately. Horizontal overflow is read from the content
38
+ * running out of room and from the shadows at the edges; nothing in the system
39
+ * wants a second bar, and adding one later takes nothing away.
40
+ *
41
+ * The bar is `aria-hidden`: it is a picture of a scroll position, and the
42
+ * scrollport it belongs to is already reachable and scrollable from the
43
+ * keyboard. `mousedown`/`mousemove` are what drive the thumb, so a coarse
44
+ * pointer gets nothing from it and keeps the native scrolling it already had.
45
+ */
46
+ export declare const Scrollbar: import('react').ForwardRefExoticComponent<ScrollbarProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,2 @@
1
+ export { Scrollbar, type ScrollbarProps } from './Scrollbar.tsx';
2
+ export { useScrollbar, SCROLLBAR_THICKNESS, type ScrollbarPort, type UseScrollbarReturn, } from './useScrollbar.ts';
@@ -0,0 +1,52 @@
1
+ import { RefObject } from 'react';
2
+ /**
3
+ * The track's own width, `w-3`.
4
+ *
5
+ * Exported because a parent that places the bar by its *left* edge has to pull
6
+ * it back by the full width to sit inside the scrollport rather than alongside
7
+ * it, and the number it pulls back by has to be the one the stylesheet uses.
8
+ */
9
+ export declare const SCROLLBAR_THICKNESS = 12;
10
+ /**
11
+ * The element that scrolls, as a ref or as the element itself. A ref because
12
+ * that is what a parent normally has, and a `useRef` never re-renders anything
13
+ * when it fills in.
14
+ */
15
+ export type ScrollbarPort = RefObject<HTMLElement | null> | HTMLElement | null | undefined;
16
+ export interface UseScrollbarReturn {
17
+ /** Shown while scrolling, while dragging, and near the right edge. */
18
+ visible: boolean;
19
+ /** The thumb is being dragged. */
20
+ dragging: boolean;
21
+ /** The pointer is in the strip the bar occupies, so the bar widens. */
22
+ near: boolean;
23
+ /** Where the thumb goes, in pixels. A height of zero means no thumb at all. */
24
+ thumb: {
25
+ top: number;
26
+ height: number;
27
+ };
28
+ /** Recompute the thumb from the port's current scroll metrics. */
29
+ measure: () => void;
30
+ /** `onMouseDown` on the thumb. */
31
+ startDrag: (event: React.MouseEvent) => void;
32
+ /** `onMouseEnter` on the thumb. See `near`. */
33
+ approach: () => void;
34
+ }
35
+ /**
36
+ * The behaviour of an overlay scrollbar, with no opinion about where it sits.
37
+ * Mirrors `@sesame/orxata-core` useScrollbar.
38
+ *
39
+ * Geometry only goes one way here: the track is positioned and sized by CSS,
40
+ * and this reads back the height it ended up with. That is what keeps the
41
+ * placement out — a virtualised table subtracts its `<thead>` and compensates
42
+ * for an `offsetParent`, a chat composer does neither, and neither of those
43
+ * facts has to be expressible here for both to work.
44
+ *
45
+ * Both elements are watched for resize, which is also what makes the
46
+ * measurement land in the right frame: a `<textarea>` that auto-grows writes
47
+ * its own height, the track follows it, and the resize callback runs after that
48
+ * write and before the paint. That matters more here than on the Vue side —
49
+ * a child's effects run before its parent's, so the bar cannot be ordered
50
+ * after the growth by declaration order alone.
51
+ */
52
+ export declare const useScrollbar: (port: ScrollbarPort, track: RefObject<HTMLElement | null>) => UseScrollbarReturn;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Hooks shared by more than one component — the React side of the Vue
3
+ * package's `src/composables/`, which is the rename the parity rules apply
4
+ * (`composables` ↔ `hooks`, see `packages/eslint-plugin/src/parity/compare.js`).
5
+ *
6
+ * A hook lives beside its component until a second one needs it; at that point
7
+ * it belongs here, because the alternative is a component importing out of a
8
+ * sibling's folder and a reader having to know that `Textarea`'s sizing is
9
+ * really `ChatInput`'s. See `.claude/rules/component-architecture.md` §13.
10
+ */
11
+ export { useAutoGrow } from './useAutoGrow.ts';
12
+ export type { UseAutoGrowReturn } from './useAutoGrow.ts';
@@ -0,0 +1,22 @@
1
+ import { RefObject } from 'react';
2
+ export interface UseAutoGrowReturn {
3
+ /** Re-measure and re-apply the height. Cheap; call it on every keystroke. */
4
+ resize: () => void;
5
+ }
6
+ /**
7
+ * Grows a `<textarea>` with its content up to `maxRows`, then lets it scroll.
8
+ * Mirrors `@sesame/orxata-core` useAutoGrow.
9
+ *
10
+ * Measuring is the whole trick and it has one rule: the height has to be
11
+ * cleared before `scrollHeight` is read. `scrollHeight` never reports less
12
+ * than the element's own height, so measuring against the height left over
13
+ * from the previous keystroke makes the field a one-way ratchet — it grows
14
+ * when text is added and never shrinks when it is deleted.
15
+ *
16
+ * The cap is applied here rather than as a CSS `max-height` so that the
17
+ * overflow can be switched with it. A permanently scrollable textarea shows a
18
+ * scrollbar gutter in the browsers that reserve one, which moves the text by a
19
+ * few pixels for as long as the field is a single line — visible, and wrong
20
+ * against a design that has no scrollbar in it.
21
+ */
22
+ export declare const useAutoGrow: (element: RefObject<HTMLTextAreaElement | null>, value: string, maxRows: number) => UseAutoGrowReturn;
@@ -0,0 +1,3 @@
1
+ export type ListboxEmptyProps = React.ComponentPropsWithoutRef<'div'>;
2
+ /** Shown in place of the rows when a search matched nothing. Internal. */
3
+ export declare const ListboxEmpty: import('react').ForwardRefExoticComponent<Omit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,23 @@
1
+ export interface ListboxItemProps extends React.ComponentPropsWithoutRef<'div'> {
2
+ /**
3
+ * The check mark, for lists that have one. Pass it — even as a fragment that
4
+ * renders nothing while the row is unchecked — and the row reserves the space
5
+ * on its right; leave it out and the row sits flush.
6
+ */
7
+ indicator?: React.ReactNode;
8
+ }
9
+ /**
10
+ * One row of a list: `Select`'s option, `Combobox`'s option, `SearchPanel`'
11
+ * result. Port of `core/src/internal/Listbox/ListboxItem`.
12
+ *
13
+ * Whether the row leaves room on the right is decided by the presence of the
14
+ * `indicator` prop, not by whether the row is currently checked — a list with
15
+ * check marks always passes it and a list without one never does, so the gap
16
+ * is stable instead of appearing the moment a row becomes checked.
17
+ *
18
+ * The indicator wrapper is rendered here rather than left to the caller so the
19
+ * positioning lives in one file. That matters less in React than in Vue, where
20
+ * scoped CSS would simply not reach an element authored elsewhere, but keeping
21
+ * the two ports the same shape is what makes them comparable.
22
+ */
23
+ export declare const ListboxItem: import('react').ForwardRefExoticComponent<ListboxItemProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,9 @@
1
+ export type ListboxLabelProps = React.ComponentPropsWithoutRef<'div'>;
2
+ /**
3
+ * The heading above a group of rows. Internal — see `Surface.tsx`.
4
+ *
5
+ * Content is `children`, never a string prop: the heading is the caller's word
6
+ * for their own category, and a design system that took it as a prop would be
7
+ * deciding how it can be written.
8
+ */
9
+ export declare const ListboxLabel: import('react').ForwardRefExoticComponent<Omit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,3 @@
1
+ export type ListboxSeparatorProps = React.ComponentPropsWithoutRef<'div'>;
2
+ /** The rule between two groups of rows. Internal — see `Surface.tsx`. */
3
+ export declare const ListboxSeparator: import('react').ForwardRefExoticComponent<Omit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import('react').RefAttributes<HTMLDivElement>>;