@sesamehr/react-design-system 2.0.0-beta.1 → 2.0.0-beta.10

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 (132) hide show
  1. package/README.md +34 -0
  2. package/dist/Button/ActionItem/ActionItem.d.ts +40 -0
  3. package/dist/Button/ActionItem/index.d.ts +1 -0
  4. package/dist/Button/IconButton/IconButton.d.ts +32 -0
  5. package/dist/Button/IconButton/index.d.ts +27 -0
  6. package/dist/Data/Table/Table.d.ts +1 -1
  7. package/dist/Data/Table/TableBody/TableBody.d.ts +3 -0
  8. package/dist/Data/Table/TableHead/TableHead.d.ts +25 -1
  9. package/dist/Data/Table/TableScrollbar/TableScrollbar.d.ts +21 -0
  10. package/dist/Data/Table/TableSelectCell/TableSelectCell.d.ts +8 -0
  11. package/dist/Data/Table/TableVirtualized/TableVirtualized.d.ts +45 -0
  12. package/dist/Data/Table/TableVirtualized/index.d.ts +2 -0
  13. package/dist/Data/Table/hooks/index.d.ts +3 -0
  14. package/dist/Data/Table/hooks/useDragToScroll/useDragToScroll.d.ts +12 -2
  15. package/dist/Data/Table/hooks/useTableVirtual/context.d.ts +16 -0
  16. package/dist/Data/Table/hooks/useTableVirtual/index.d.ts +3 -0
  17. package/dist/Data/Table/hooks/useTableVirtual/useTableVirtual.d.ts +36 -0
  18. package/dist/Data/Table/hooks/useTableVirtual/useTableVirtual.types.d.ts +74 -0
  19. package/dist/Data/Table/index.d.ts +2 -2
  20. package/dist/Display/Avatar/AvatarSkeleton/AvatarSkeleton.d.ts +30 -0
  21. package/dist/Display/Avatar/index.d.ts +2 -0
  22. package/dist/Display/AvatarDetails/AvatarDetails.d.ts +68 -0
  23. package/dist/Display/AvatarDetails/index.d.ts +1 -0
  24. package/dist/Display/Badge/Badge.d.ts +3 -2
  25. package/dist/Display/Badge/index.d.ts +10 -1
  26. package/dist/Display/ChatBubble/ChatBubble.d.ts +28 -0
  27. package/dist/Display/ChatBubble/ChatBubbleStatus/ChatBubbleStatus.d.ts +8 -0
  28. package/dist/Display/ChatBubble/ChatBubbleStatus/index.d.ts +10 -0
  29. package/dist/Display/ChatBubble/index.d.ts +44 -0
  30. package/dist/Display/Chip/Chip.d.ts +21 -5
  31. package/dist/Display/Chip/index.d.ts +19 -10
  32. package/dist/Display/FileCard/FileCard.d.ts +66 -0
  33. package/dist/Display/FileCard/index.d.ts +1 -0
  34. package/dist/Display/FilePreviewCard/FilePreviewCard.d.ts +49 -0
  35. package/dist/Display/FilePreviewCard/index.d.ts +1 -0
  36. package/dist/Feedback/Alert/AlertIcon.d.ts +1 -1
  37. package/dist/Feedback/Dialog/DialogBody.d.ts +18 -0
  38. package/dist/Feedback/Dialog/index.d.ts +1 -0
  39. package/dist/Feedback/Loader/Loader.d.ts +16 -0
  40. package/dist/Feedback/Loader/index.d.ts +2 -0
  41. package/dist/Feedback/Popover/PopoverContent.d.ts +0 -1
  42. package/dist/Feedback/Progress/Progress.d.ts +3 -0
  43. package/dist/Feedback/Progress/index.d.ts +17 -0
  44. package/dist/Feedback/Skeleton/Skeleton.d.ts +20 -0
  45. package/dist/Feedback/Skeleton/index.d.ts +2 -0
  46. package/dist/Feedback/Tooltip/TooltipContent.d.ts +0 -1
  47. package/dist/Forms/Field/Counter/Counter.d.ts +9 -0
  48. package/dist/Forms/Field/Counter/index.d.ts +13 -0
  49. package/dist/Forms/Field/FieldGroup/FieldGroup.d.ts +16 -1
  50. package/dist/Forms/Field/FieldLabel/FieldLabel.d.ts +16 -0
  51. package/dist/Forms/Field/FieldMessage/FieldMessage.d.ts +22 -2
  52. package/dist/Forms/Field/index.d.ts +1 -0
  53. package/dist/Forms/Field/useFieldGroup.d.ts +108 -0
  54. package/dist/Forms/Inputs/Calendar/Calendar.d.ts +55 -0
  55. package/dist/Forms/Inputs/Calendar/CalendarDay.d.ts +37 -0
  56. package/dist/Forms/Inputs/Calendar/CalendarRange.d.ts +56 -0
  57. package/dist/Forms/Inputs/Calendar/constraints.d.ts +19 -0
  58. package/dist/Forms/Inputs/Calendar/date.d.ts +28 -0
  59. package/dist/Forms/Inputs/Calendar/index.d.ts +21 -0
  60. package/dist/Forms/Inputs/Calendar/nav.d.ts +34 -0
  61. package/dist/Forms/Inputs/Calendar/testids.d.ts +28 -0
  62. package/dist/Forms/Inputs/Calendar/useCalendarLocale.d.ts +40 -0
  63. package/dist/Forms/Inputs/ChatInput/ChatInput.d.ts +68 -0
  64. package/dist/Forms/Inputs/ChatInput/index.d.ts +46 -0
  65. package/dist/Forms/Inputs/Combobox/ComboboxCancel.d.ts +14 -0
  66. package/dist/Forms/Inputs/Combobox/ComboboxChipsInput.d.ts +4 -0
  67. package/dist/Forms/Inputs/Combobox/ComboboxContent.d.ts +0 -6
  68. package/dist/Forms/Inputs/Combobox/ComboboxGroup.d.ts +6 -2
  69. package/dist/Forms/Inputs/Combobox/ComboboxInput.d.ts +6 -1
  70. package/dist/Forms/Inputs/Combobox/ComboboxItem.d.ts +4 -0
  71. package/dist/Forms/Inputs/Combobox/ComboboxItemEdit.d.ts +124 -0
  72. package/dist/Forms/Inputs/Combobox/context.d.ts +15 -1
  73. package/dist/Forms/Inputs/Combobox/index.d.ts +2 -0
  74. package/dist/Forms/Inputs/Combobox/useChipOverflow.d.ts +30 -0
  75. package/dist/Forms/Inputs/Combobox/useComboboxEditing.d.ts +40 -0
  76. package/dist/Forms/Inputs/Combobox/useComboboxLabels.d.ts +21 -0
  77. package/dist/Forms/Inputs/DatePicker/DatePicker.d.ts +51 -0
  78. package/dist/Forms/Inputs/DatePicker/DatePickerHeader.d.ts +65 -0
  79. package/dist/Forms/Inputs/DatePicker/DatePickerMonthSelect.d.ts +27 -0
  80. package/dist/Forms/Inputs/DatePicker/DatePickerTrigger.d.ts +29 -0
  81. package/dist/Forms/Inputs/DatePicker/DatePickerYearSelect.d.ts +25 -0
  82. package/dist/Forms/Inputs/DatePicker/DateRangePicker.d.ts +61 -0
  83. package/dist/Forms/Inputs/DatePicker/format.d.ts +44 -0
  84. package/dist/Forms/Inputs/DatePicker/index.d.ts +6 -0
  85. package/dist/Forms/Inputs/DatePicker/month.d.ts +28 -0
  86. package/dist/Forms/Inputs/DatePicker/useDatePickerHeader.d.ts +23 -0
  87. package/dist/Forms/Inputs/InputOtp/InputOtp.d.ts +7 -0
  88. package/dist/Forms/Inputs/InputPassword/InputPassword.d.ts +9 -1
  89. package/dist/Forms/Inputs/InputText/InputText.d.ts +9 -0
  90. package/dist/Forms/Inputs/SearchBar/SearchBar.d.ts +35 -0
  91. package/dist/Forms/Inputs/SearchBar/index.d.ts +22 -0
  92. package/dist/Forms/Inputs/SearchBar/useSearchDelay.d.ts +24 -0
  93. package/dist/Forms/Inputs/SearchPanel/SearchPanel.d.ts +71 -0
  94. package/dist/Forms/Inputs/SearchPanel/SearchPanelEmpty.d.ts +16 -0
  95. package/dist/Forms/Inputs/SearchPanel/SearchPanelGroup.d.ts +14 -0
  96. package/dist/Forms/Inputs/SearchPanel/SearchPanelInput.d.ts +31 -0
  97. package/dist/Forms/Inputs/SearchPanel/SearchPanelItem.d.ts +20 -0
  98. package/dist/Forms/Inputs/SearchPanel/SearchPanelList.d.ts +11 -0
  99. package/dist/Forms/Inputs/SearchPanel/context.d.ts +11 -0
  100. package/dist/Forms/Inputs/SearchPanel/index.d.ts +6 -0
  101. package/dist/Forms/Inputs/Select/SelectContent.d.ts +0 -4
  102. package/dist/Forms/Inputs/Select/SelectGroup.d.ts +6 -2
  103. package/dist/Forms/Inputs/Select/SelectItem.d.ts +6 -0
  104. package/dist/Forms/Inputs/Textarea/Textarea.d.ts +69 -0
  105. package/dist/Forms/Inputs/Textarea/index.d.ts +21 -0
  106. package/dist/Forms/Inputs/Toggle/RadioButton/RadioGroup.d.ts +5 -0
  107. package/dist/Forms/Inputs/Toggle/ToggleLabel/ToggleLabel.d.ts +10 -1
  108. package/dist/Forms/Inputs/index.d.ts +6 -0
  109. package/dist/Layout/Scrollbar/Scrollbar.d.ts +46 -0
  110. package/dist/Layout/Scrollbar/index.d.ts +2 -0
  111. package/dist/Layout/Scrollbar/useScrollbar.d.ts +52 -0
  112. package/dist/hooks/index.d.ts +12 -0
  113. package/dist/hooks/useAutoGrow.d.ts +22 -0
  114. package/dist/internal/Listbox/ListboxEmpty.d.ts +3 -0
  115. package/dist/internal/Listbox/ListboxItem.d.ts +23 -0
  116. package/dist/internal/Listbox/ListboxLabel.d.ts +9 -0
  117. package/dist/internal/Listbox/ListboxSeparator.d.ts +3 -0
  118. package/dist/internal/Listbox/ListboxViewport.d.ts +9 -0
  119. package/dist/internal/Surface/Surface.d.ts +35 -0
  120. package/dist/internal/index.d.ts +13 -0
  121. package/dist/lib/cssModules.d.ts +13 -0
  122. package/dist/main.d.ts +18 -7
  123. package/dist/preflight.css +391 -0
  124. package/dist/react-design-system.css +1 -1
  125. package/dist/react-design-system.js +17345 -10589
  126. package/dist/react-design-system.umd.cjs +164 -117
  127. package/package.json +4 -2
  128. package/src/assets/styles/theme-v2.css +6 -6
  129. package/src/assets/styles/theme-v3.css +7 -4
  130. package/dist/Data/Table/TableAvatar/TableAvatar.d.ts +0 -14
  131. package/dist/Display/Tag/Tag.d.ts +0 -19
  132. package/dist/Display/Tag/index.d.ts +0 -2
@@ -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,11 @@
1
+ import { CommandList } from 'cmdk';
2
+ export type SearchPanelListProps = React.ComponentPropsWithoutRef<typeof CommandList>;
3
+ export declare const SearchPanelList: import('react').ForwardRefExoticComponent<Omit<{
4
+ children?: React.ReactNode;
5
+ } & Pick<Pick<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof import('react').HTMLAttributes<HTMLDivElement>> & {
6
+ ref?: React.Ref<HTMLDivElement>;
7
+ } & {
8
+ asChild?: boolean;
9
+ }, "asChild" | "key" | keyof import('react').HTMLAttributes<HTMLDivElement>> & {
10
+ label?: string;
11
+ } & 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';
@@ -4,8 +4,4 @@ export interface SelectContentProps extends Omit<React.ComponentPropsWithoutRef<
4
4
  sideOffset?: number;
5
5
  align?: 'start' | 'center' | 'end';
6
6
  }
7
- /**
8
- * The floating list. Always positioned against the trigger rather than over it,
9
- * so the popup matches the trigger's width and never covers it.
10
- */
11
7
  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>>;
@@ -3,6 +3,12 @@ export { InputText, type InputTextProps } from './InputText/InputText.tsx';
3
3
  export * from './InputNumber/index.ts';
4
4
  export * from './InputPassword/index.ts';
5
5
  export * from './InputOtp/index.ts';
6
+ export * from './Calendar/index.ts';
6
7
  export * from './Combobox/index.ts';
8
+ export * from './ChatInput/index.ts';
9
+ export * from './DatePicker/index.ts';
10
+ export * from './SearchBar/index.ts';
11
+ export * from './SearchPanel/index.ts';
7
12
  export * from './Select/index.ts';
13
+ export * from './Textarea/index.ts';
8
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>>;
@@ -0,0 +1,9 @@
1
+ export type ListboxViewportProps = React.ComponentPropsWithoutRef<'div'>;
2
+ /**
3
+ * The padded, scrolling box the rows sit in. Internal — see `Surface.tsx`.
4
+ *
5
+ * Reached through `asChild` from Radix's `Select.Viewport` or cmdk's
6
+ * `Command.List`, both of which own the scrolling behaviour and neither of
7
+ * which agrees on a name.
8
+ */
9
+ export declare const ListboxViewport: import('react').ForwardRefExoticComponent<Omit<import('react').DetailedHTMLProps<import('react').HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,35 @@
1
+ export interface SurfaceProps extends React.ComponentPropsWithoutRef<'div'> {
2
+ /**
3
+ * Drawn free of any trigger rather than tethered to one: no border, a deeper
4
+ * shadow. The command palette is the case.
5
+ */
6
+ floating?: boolean;
7
+ /**
8
+ * Joined to the edge of whatever it drops out of, rather than separated from
9
+ * it by a gap: the corners on the side it meets go square, so the two boxes
10
+ * read as one card. `SearchPanel`'s results are the case.
11
+ */
12
+ attached?: boolean;
13
+ }
14
+ /**
15
+ * The floating surface: the card a `Popover`, a `Select`, a `Combobox` or a
16
+ * `SearchPanel` panel is drawn on. Port of `core/src/internal/Surface`.
17
+ *
18
+ * Internal on purpose. It carries no portal and no positioning — those belong
19
+ * to whichever primitive family opened it, and they are not interchangeable.
20
+ * What is interchangeable is the *look*: the fill, the border, the radius, the
21
+ * shadow and the open/close animation. Each content component keeps its own
22
+ * primitive as the root and reaches this element through `asChild`, so the
23
+ * primitive's `data-state` and `data-side` land on the div below and the
24
+ * animation selectors see them.
25
+ *
26
+ * It is not exported from `main.ts`. On its own it is an inert div: no
27
+ * keyboard, no focus management, no positioning. The public way to get one
28
+ * stays `OxPopover`, `OxSelect`, `OxCombobox` or `OxSearchPanel`.
29
+ *
30
+ * `forwardRef` is not optional here: `asChild` composes the primitive's ref
31
+ * onto this component, and a plain function component would drop it — the
32
+ * popover would have nothing to measure and would position itself at the
33
+ * origin.
34
+ */
35
+ export declare const Surface: import('react').ForwardRefExoticComponent<SurfaceProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Shared presentational parts. Deliberately absent from `src/main.ts`: each is
3
+ * an inert element with no keyboard, no roles and no positioning, so publishing
4
+ * one would ship something that looks like an option and behaves like a div.
5
+ * The public way to build a list stays `OxSelect`, `OxCombobox` or
6
+ * `OxSearchPanel`, each of which pairs these with its own primitive.
7
+ */
8
+ export { Surface, type SurfaceProps } from './Surface/Surface.tsx';
9
+ export { ListboxEmpty, type ListboxEmptyProps, } from './Listbox/ListboxEmpty.tsx';
10
+ export { ListboxItem, type ListboxItemProps } from './Listbox/ListboxItem.tsx';
11
+ export { ListboxLabel, type ListboxLabelProps, } from './Listbox/ListboxLabel.tsx';
12
+ export { ListboxSeparator, type ListboxSeparatorProps, } from './Listbox/ListboxSeparator.tsx';
13
+ export { ListboxViewport, type ListboxViewportProps, } from './Listbox/ListboxViewport.tsx';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Maps the class names a `cva()` returns through a CSS module's hashed names.
3
+ *
4
+ * The variant objects are copied verbatim between the two packages —
5
+ * `parity-variants` compares them as source text — so they have to hold the
6
+ * same literal strings on both sides: `'ox-button--default'`, not a
7
+ * `styles.oxButtonDefault` lookup that only exists here. The lookup happens
8
+ * afterwards, which is what this is.
9
+ *
10
+ * A name the module does not define passes through untouched, so a caller's own
11
+ * class survives being handed to it.
12
+ */
13
+ export declare const moduleClasses: (styles: Record<string, string>, names: string | undefined) => string;