@ntmats/hc-ui 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,10 +1,13 @@
1
- import { type ButtonHTMLAttributes, type HTMLAttributes, type ReactNode } from 'react';
2
- export type SurfaceTone = 'page' | 'main' | 'content' | 'elevated' | 'overlay';
1
+ import { type ButtonHTMLAttributes, type HTMLAttributes, type InputHTMLAttributes, type KeyboardEvent, type MouseEvent, type ReactElement, type ReactNode, type Ref } from 'react';
2
+ export type SurfaceTone = 'page' | 'main' | 'content' | 'elevated' | 'overlay' | 'card';
3
3
  export interface SurfaceProps extends HTMLAttributes<HTMLDivElement> {
4
4
  tone?: SurfaceTone;
5
+ /** Corner radius of the rounded tones (`content`, `elevated`, `overlay`, `card`): 16, 20 or 24px.
6
+ * Defaults to `md` (16px), and to `lg` (20px) for `card`, as HCUI's `surface` mixin. */
7
+ radius?: 'md' | 'lg' | 'xl';
5
8
  }
6
9
  /** Semantic color role only; placement, elevation and overlay behavior stay local. */
7
- export declare function Surface({ tone, className, ...props }: SurfaceProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare function Surface({ tone, radius, className, ...props }: SurfaceProps): import("react/jsx-runtime").JSX.Element;
8
11
  export interface SegmentedOption<T extends string> {
9
12
  value: T;
10
13
  label: string;
@@ -15,21 +18,34 @@ export interface SegmentedControlProps<T extends string> extends Omit<HTMLAttrib
15
18
  label: string;
16
19
  value: T;
17
20
  options: readonly SegmentedOption<T>[];
18
- onChange: (value: T) => void;
21
+ /** NoInfer: `T` comes from `value` and `options`, so a state setter cannot widen it to string. */
22
+ onChange: (value: NoInfer<T>) => void;
19
23
  density?: 'comfortable' | 'compact';
20
24
  orientation?: 'horizontal' | 'vertical';
21
25
  /** Icon-only options: labels stay as accessible names and tooltips. */
22
26
  hideLabels?: boolean;
27
+ /** `static` (default): the chosen option is raised in place. `slide`: one knob slides
28
+ * under the options, as HCUI's point cost toggle; options are laid out at equal width. */
29
+ indicator?: 'static' | 'slide';
30
+ /** Every option as wide as the widest one (or as `optionWidth`). */
31
+ equalWidth?: boolean;
32
+ /** Fixed width of each option in px; implies `equalWidth`. */
33
+ optionWidth?: number;
23
34
  }
24
35
  /** Controlled, single-choice control with radio semantics and roving focus. */
25
- export declare function SegmentedControl<T extends string>({ label, value, options, onChange, density, orientation, hideLabels, className, ...props }: SegmentedControlProps<T>): import("react/jsx-runtime").JSX.Element;
36
+ export declare function SegmentedControl<T extends string>({ label, value, options, onChange, density, orientation, hideLabels, indicator, equalWidth, optionWidth, className, style, ...props }: SegmentedControlProps<T>): import("react/jsx-runtime").JSX.Element;
26
37
  export type ButtonVariant = 'secondary' | 'primary' | 'outline' | 'danger';
27
38
  export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
28
39
  variant?: ButtonVariant;
29
- size?: 'md' | 'lg';
40
+ /** 36, 44 or 52px. */
41
+ size?: 'sm' | 'md' | 'lg';
30
42
  /** Decorative icon before the label; pass it already sized (18px in HCUI). */
31
43
  icon?: ReactNode;
44
+ /** Decorative icon after the label. */
45
+ iconEnd?: ReactNode;
32
46
  fullWidth?: boolean;
47
+ /** Busy state: keeps focus and label, shows a spinner and ignores clicks. */
48
+ loading?: boolean;
33
49
  }
34
50
  /** Pill button. The caller supplies the label text and handles the action. */
35
51
  export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -40,16 +56,115 @@ export interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonEle
40
56
  /** 36, 40 or 44px. The 36px size keeps a 44px touch target. */
41
57
  size?: 'sm' | 'md' | 'lg';
42
58
  tone?: 'primary' | 'secondary';
59
+ /** Fill: `muted` (default, HCUI `icon-button`), `surface` (surface with the card ring
60
+ * and shadow, HCUI's mobile theme button), `outline` (surface with a 1px border ring,
61
+ * HCUI's mobile sort button) or `ghost` (no fill until hover). */
62
+ variant?: 'muted' | 'surface' | 'outline' | 'ghost';
63
+ /** `circle` (default) or `rounded` (a 12px-radius square, HCUI's desktop utilities). */
64
+ shape?: 'circle' | 'rounded';
65
+ /** Toggle state (`aria-pressed`); omit for a plain action. */
66
+ selected?: boolean;
67
+ /** When set, the icon is keyed by it and enters with a short turn and fade each time it
68
+ * changes (and on first render), as HCUI's theme button swaps sun and moon. */
69
+ iconKey?: string | number;
43
70
  }
44
71
  /** Round icon-only button (back, close, options). */
45
72
  export declare const IconButton: import("react").ForwardRefExoticComponent<IconButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
46
- export interface ChipProps extends ButtonHTMLAttributes<HTMLButtonElement> {
73
+ interface ChipBaseProps extends ButtonHTMLAttributes<HTMLButtonElement> {
47
74
  /** Toggle state for filters; omit for a plain action chip. */
48
75
  selected?: boolean;
49
76
  icon?: ReactNode;
77
+ /**
78
+ * `md` (default): 36px, 13px text (HCUI `chip`). `sm`: 36px, 12px text and 12px padding (HCUI's
79
+ * desktop filter pills and sort trigger). `xs`: 26px, 11px text, muted fill without ring (HCUI's
80
+ * desktop quick filters and chosen-value tags); selected turns to the contrast fill.
81
+ */
82
+ size?: 'md' | 'sm' | 'xs';
83
+ /** `outline` (default), or `quiet`: lighter ring and secondary text, bolder once applied (HCUI's rule filter pills). */
84
+ variant?: 'outline' | 'quiet';
85
+ /** A filter with values applied: a 1.5px ring in the primary text color (and primary, bolder text when `quiet`). */
86
+ applied?: boolean;
87
+ /**
88
+ * Muted number after the label, such as how many results a quick filter keeps. A number
89
+ * shows as given, 0 included (HCUI's quick filters show "0"); hidden when `undefined`, `null`, `false` or `''`.
90
+ */
91
+ count?: ReactNode;
92
+ /**
93
+ * Filled badge after the label: how many values a filter has applied. Shows 0 too; pass
94
+ * `undefined` (e.g. `badge={n || undefined}`) to hide it when nothing is applied.
95
+ */
96
+ badge?: ReactNode;
97
+ /**
98
+ * Down chevron at the end. On by default when the chip opens a popup (`aria-haspopup`,
99
+ * which `Menu` and `Popover` give their trigger); `aria-expanded="true"` draws the ring.
100
+ */
101
+ chevron?: boolean;
102
+ /**
103
+ * Adds a separate remove button (×) after the text. The chip then renders as a `span`
104
+ * holding the main button (ref, `onClick`, `aria-pressed`; a plain `span` when there is
105
+ * no `onClick`, as a chosen-value tag) and the remove button. `className` and `style` go on the `span`.
106
+ * Requires `clearLabel`.
107
+ */
108
+ onClear?: (event: MouseEvent<HTMLButtonElement>) => void;
109
+ /** Accessible name and tooltip of the remove button (e.g. "Remover Batman"); required with `onClear`. */
110
+ clearLabel?: string;
50
111
  }
51
- /** Filter or option chip. Selected chips are filled with the accent color. */
112
+ /** `Chip` props: with `onClear`, `clearLabel` is required, so the remove button always has a name. */
113
+ export type ChipProps = ChipBaseProps & ({
114
+ onClear?: undefined;
115
+ clearLabel?: string;
116
+ } | {
117
+ onClear?: (event: MouseEvent<HTMLButtonElement>) => void;
118
+ clearLabel: string;
119
+ });
120
+ /**
121
+ * Filter or option chip. Selected chips are filled with the accent color. As a filter
122
+ * trigger it takes a `badge` with the number of values applied, `applied`, and the
123
+ * chevron and ring of an open popup; `onClear` adds a remove button.
124
+ */
52
125
  export declare const Chip: import("react").ForwardRefExoticComponent<ChipProps & import("react").RefAttributes<HTMLButtonElement>>;
126
+ export interface FilterBarProps extends HTMLAttributes<HTMLDivElement> {
127
+ /** Accessible name of the group (e.g. "Filtros"). */
128
+ label: string;
129
+ /** Before the filters, on the same line (a search field, "Filtros avançados"). */
130
+ start?: ReactNode;
131
+ /** After the filters, at the end of the line (a sort menu, a view toggle). */
132
+ end?: ReactNode;
133
+ /** Under the line: chosen-value tags, "Limpar tudo". */
134
+ footer?: ReactNode;
135
+ /**
136
+ * `wrap`: the filters wrap onto more lines (desktop). `scroll`: one line that scrolls
137
+ * sideways with a hidden scrollbar (phones). `auto` (default): `scroll` below 960px and
138
+ * `wrap` from 960px, by a CSS media query (HCUI's cut).
139
+ */
140
+ layout?: 'wrap' | 'scroll' | 'auto';
141
+ /** Space between filters: 8px (`comfortable`, default) or 4px (`compact`, HCUI's desktop quick filters). */
142
+ density?: 'comfortable' | 'compact';
143
+ /** A band that sticks to the top of the scrolling panel, with the surface fill and 8px above and below (HCUI's desktop filter bar). The offset is `--hc-filter-bar-top`. */
144
+ sticky?: boolean;
145
+ }
146
+ /**
147
+ * Layout of a row of filters (`Chip`s) with optional `start`, `end` and `footer` slots.
148
+ * Presentation only: which filters exist, their values, the search rules and persistence
149
+ * are the caller's. A scrolling row bleeds into the page gutter by `--hc-filter-bar-bleed`.
150
+ */
151
+ export declare const FilterBar: import("react").ForwardRefExoticComponent<FilterBarProps & import("react").RefAttributes<HTMLDivElement>>;
152
+ export interface FieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
153
+ /** Accessible name. Visible above the field unless `hideLabel` is set. */
154
+ label: string;
155
+ hideLabel?: boolean;
156
+ /** 40 or 44px. */
157
+ density?: 'comfortable' | 'compact';
158
+ /** Decorative icon at the start of the field. */
159
+ icon?: ReactNode;
160
+ /** Slot at the end of the field, such as a clear button. */
161
+ end?: ReactNode;
162
+ hint?: ReactNode;
163
+ /** Error message; also marks the input as invalid. */
164
+ error?: ReactNode;
165
+ }
166
+ /** Filled pill text field. Value, validation and messages stay with the caller. */
167
+ export declare const Field: import("react").ForwardRefExoticComponent<FieldProps & import("react").RefAttributes<HTMLInputElement>>;
53
168
  export interface SkeletonProps extends HTMLAttributes<HTMLSpanElement> {
54
169
  width?: string | number;
55
170
  height?: string | number;
@@ -60,3 +175,468 @@ export interface SkeletonProps extends HTMLAttributes<HTMLSpanElement> {
60
175
  * enclosing region announces loading once (role="status" + label).
61
176
  */
62
177
  export declare function Skeleton({ width, height, circle, className, style, ...props }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
178
+ interface MeterOwnProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children' | 'role'> {
179
+ /** Current amount, such as a team's points. May exceed `max`. */
180
+ value: number;
181
+ /** The limit the value is measured against; the minimum is 0. */
182
+ max: number;
183
+ /** Spoken value (`aria-valuetext`); defaults to `"<value> / <max>"`. */
184
+ valueText?: string;
185
+ /** Bar height in px: 3 (HCUI's mobile team bar), 4 (desktop sidebar draft) or 5 (team rules bar). */
186
+ thickness?: 3 | 4 | 5;
187
+ /** Track behind the fill: `muted` (bg-muted), `border` (color-border) or `none` (fill only). */
188
+ track?: 'muted' | 'border' | 'none';
189
+ /** Leading end of the fill: rounded, or cut square against the track. */
190
+ cap?: 'round' | 'square';
191
+ /**
192
+ * Above the limit: `clamp` fills the whole bar; `mark` scales the bar to the value
193
+ * and draws a 2px line where the limit is, in the text color so it shows over the danger fill.
194
+ */
195
+ overflow?: 'clamp' | 'mark';
196
+ /** `auto` turns `over` (danger) when value > max; `default` and `over` force a tone. */
197
+ tone?: 'auto' | 'default' | 'over';
198
+ /** Transition the fill's width (none under reduced motion). */
199
+ animated?: boolean;
200
+ }
201
+ export type MeterProps = MeterOwnProps & (
202
+ /** `label` is the accessible name; the visible text ("375 / 300 pts") stays with the caller. */
203
+ {
204
+ label: string;
205
+ decorative?: false;
206
+ }
207
+ /** Hidden from assistive technology, for when the numbers are already next to it. */
208
+ | {
209
+ decorative: true;
210
+ label?: string;
211
+ });
212
+ /**
213
+ * Bar of a value against a limit (`role="meter"`), with an over-the-limit tone. It
214
+ * renders only spans, so it fits inside a button. Placement (absolute, grid) is the caller's.
215
+ */
216
+ export declare const Meter: import("react").ForwardRefExoticComponent<MeterProps & import("react").RefAttributes<HTMLSpanElement>>;
217
+ export interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'type'> {
218
+ /** 20px (touch lists, sheets) or 16px (dense desktop lists). */
219
+ size?: 'sm' | 'md';
220
+ /** Mixed state. Visual only: the value still comes from `checked`. */
221
+ indeterminate?: boolean;
222
+ }
223
+ /**
224
+ * Native checkbox laid transparently over a drawn box, so keyboard, focus, form
225
+ * and `<label>` behave as usual. Wrap it in a `<label>` (the whole row becomes the
226
+ * target) or pass `aria-label`; the caller supplies the text.
227
+ */
228
+ export declare const Checkbox: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLInputElement>>;
229
+ export interface DialogProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title' | 'open' | 'onClose' | 'onCancel'> {
230
+ /** Controlled: the dialog shows while `open` is true and plays its exit before unmounting. */
231
+ open: boolean;
232
+ /**
233
+ * Asked to close: Esc and a backdrop click (when `dismissible`) and the close
234
+ * button. The dialog stays open until the caller sets `open` to false.
235
+ */
236
+ onClose: () => void;
237
+ /** Visible heading; it names the dialog (`aria-labelledby`). */
238
+ title: ReactNode;
239
+ /** Short text under the title; it describes the dialog (`aria-describedby`). */
240
+ description?: ReactNode;
241
+ children?: ReactNode;
242
+ /** Slot for the buttons (Cancel | Confirm). */
243
+ actions?: ReactNode;
244
+ /** Shows a close button with this accessible name. No close button by default. */
245
+ closeLabel?: string;
246
+ /** Icon for the close button; an inline X by default. */
247
+ closeIcon?: ReactNode;
248
+ /** `sm`: 400px confirmation (HCUI ConfirmDialog). `md`: 440px panel with header, scrolling body and footer (HCUI Sheet on desktop). */
249
+ size?: 'sm' | 'md';
250
+ /** Esc and a backdrop click ask to close. Default true. */
251
+ dismissible?: boolean;
252
+ }
253
+ /**
254
+ * Centered modal dialog on the native `<dialog>` and `showModal()`: the browser
255
+ * keeps it in the top layer, makes the rest of the page inert and traps Tab.
256
+ * The caller owns `open` and the texts; the dialog only asks to close.
257
+ */
258
+ export declare const Dialog: import("react").ForwardRefExoticComponent<DialogProps & import("react").RefAttributes<HTMLDialogElement>>;
259
+ export type SheetPresentation = 'bottom' | 'center' | 'auto';
260
+ export interface SheetProps extends Omit<DialogProps, 'size' | 'actions' | 'description' | 'closeLabel'> {
261
+ /** Accessible name of the close button, which every sheet shows. */
262
+ closeLabel: string;
263
+ /** Slot under the scrolling body, for the buttons (Clear | Done). */
264
+ footer?: ReactNode;
265
+ /**
266
+ * `bottom`: rises from the bottom edge, full width, with a grabber and drag to close.
267
+ * `center`: the centered panel (`Dialog size="md"`). `auto` (default): `bottom` below
268
+ * 960px and `center` from 960px, by a CSS media query (HCUI's cut).
269
+ */
270
+ presentation?: SheetPresentation;
271
+ /** Maximum width of the centered panel, in px. Default 440. */
272
+ width?: number;
273
+ }
274
+ /**
275
+ * Modal sheet on the `Dialog` base (native `<dialog>`, `showModal()`): the same focus,
276
+ * Tab trap, Esc, focus return, scroll lock, stacking and controlled `open`/`onClose`.
277
+ * At the bottom it has a grabber, drag to close (past 20% of its height or a quick
278
+ * flick) and drag up from the grabber or header to full height; content that has
279
+ * scrolled down scrolls back before the sheet moves.
280
+ */
281
+ export declare const Sheet: import("react").ForwardRefExoticComponent<SheetProps & import("react").RefAttributes<HTMLDialogElement>>;
282
+ export interface SheetOption<T extends string> {
283
+ value: T;
284
+ label: ReactNode;
285
+ /** Second line, in a muted color. */
286
+ hint?: ReactNode;
287
+ /** Decorative icon before the text; pass it already sized (18px in HCUI). */
288
+ icon?: ReactNode;
289
+ disabled?: boolean;
290
+ }
291
+ interface SheetChoiceProps<T extends string> extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'role'> {
292
+ /** Accessible name of the group. */
293
+ label: string;
294
+ /** The chosen value; one that matches no option leaves the group with none chosen. */
295
+ value: T;
296
+ /** NoInfer: `T` comes from `value` and `options`, so a state setter cannot widen it to string. */
297
+ onChange: (value: NoInfer<T>) => void;
298
+ }
299
+ export interface SheetOptionsProps<T extends string> extends SheetChoiceProps<T> {
300
+ options: readonly SheetOption<T>[];
301
+ /**
302
+ * `card` (default): rows on a `bg-muted-2` card, as HCUI's SheetOptions. `list`: plain
303
+ * 48px rows divided by a line, the chosen one bolder with a 20px check in the text color,
304
+ * as the list of HCUI's mobile sort sheet (`SortButton`).
305
+ */
306
+ variant?: 'card' | 'list';
307
+ }
308
+ /** Single-choice list of rows with a check (HCUI's SheetOptions), for inside a sheet. */
309
+ export declare function SheetOptions<T extends string>({ label, options, value, onChange, variant, className, ...props }: SheetOptionsProps<T>): import("react/jsx-runtime").JSX.Element;
310
+ export interface SheetChipsProps<T extends string> extends SheetChoiceProps<T> {
311
+ options: readonly {
312
+ value: T;
313
+ label: ReactNode;
314
+ disabled?: boolean;
315
+ }[];
316
+ }
317
+ /** Single-choice group of `Chip`s that wrap (HCUI's SheetChips), for inside a sheet. */
318
+ export declare function SheetChips<T extends string>({ label, options, value, onChange, className, ...props }: SheetChipsProps<T>): import("react/jsx-runtime").JSX.Element;
319
+ export type PopupSide = 'bottom' | 'top';
320
+ export type PopupAlign = 'start' | 'end';
321
+ /** Props a trigger receives: spread them on the element that opens the popup. */
322
+ export interface PopupTriggerProps {
323
+ ref: (node: HTMLElement | null) => void;
324
+ 'aria-haspopup': 'menu' | 'dialog';
325
+ 'aria-expanded': boolean;
326
+ 'aria-controls': string | undefined;
327
+ onClick: (event: MouseEvent<HTMLElement>) => void;
328
+ onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
329
+ }
330
+ /** An element (it gets the props merged in) or a render function that spreads them. */
331
+ export type PopupTrigger = ReactElement | ((props: PopupTriggerProps, state: {
332
+ open: boolean;
333
+ }) => ReactNode);
334
+ interface PopupBaseProps {
335
+ /** Controlled open state; omit it (and use `defaultOpen`) to let the popup own it. */
336
+ open?: boolean;
337
+ defaultOpen?: boolean;
338
+ onOpenChange?: (open: boolean) => void;
339
+ /** Preferred side; it flips when there is no room. Default `bottom`. */
340
+ side?: PopupSide;
341
+ /** Edge shared with the trigger; it flips when there is no room. Default `end`. */
342
+ align?: PopupAlign;
343
+ /**
344
+ * Render in a portal: `true` uses the trigger's open `<dialog>` (so it stays in the
345
+ * top layer) or `document.body`; an element is used as is. The portal keeps
346
+ * `data-hc-ds` and the trigger's `data-theme`. Default false: the popup is placed
347
+ * right after the trigger and inherits everything.
348
+ */
349
+ portal?: boolean | HTMLElement;
350
+ /** Theme for the portal; read from the trigger's scope by default. */
351
+ theme?: 'light' | 'dark';
352
+ /** Class of the wrapper around the trigger (the popup's positioning box). */
353
+ anchorClassName?: string;
354
+ }
355
+ type Rect = {
356
+ top: number;
357
+ right: number;
358
+ bottom: number;
359
+ left: number;
360
+ };
361
+ /** Keeps the preferred side and edge when the popup fits; otherwise flips to the roomier one. */
362
+ export declare function placePopup(trigger: Rect, size: {
363
+ width: number;
364
+ height: number;
365
+ }, boundary: Rect, side: PopupSide, align: PopupAlign): {
366
+ side: PopupSide;
367
+ align: PopupAlign;
368
+ };
369
+ export interface MenuProps extends PopupBaseProps, Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'children'> {
370
+ /** Accessible name of the menu (`aria-label`). */
371
+ label: string;
372
+ /** The button that opens it. It gets `aria-haspopup="menu"`, `aria-expanded`, `aria-controls` and the handlers. */
373
+ trigger: PopupTrigger;
374
+ children: ReactNode;
375
+ }
376
+ /**
377
+ * Menu button: actions (`MenuItem`), one choice (`MenuRadioGroup` + `MenuItemRadio`),
378
+ * toggles (`MenuItemCheckbox`), `MenuSeparator` and `MenuLabel`. Arrows, Home/End and
379
+ * typing move between items; Enter/Space choose; Esc closes and returns focus to the
380
+ * trigger; Tab closes and moves on; a press outside closes.
381
+ */
382
+ export declare const Menu: import("react").ForwardRefExoticComponent<MenuProps & import("react").RefAttributes<HTMLDivElement>>;
383
+ interface MenuItemBaseProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'role' | 'onSelect'> {
384
+ /** Decorative icon at the end of the row; pass it already sized (15px in HCUI). */
385
+ icon?: ReactNode;
386
+ /** Second line in muted, smaller text. */
387
+ hint?: ReactNode;
388
+ /** Destructive action: drawn in the danger color. */
389
+ danger?: boolean;
390
+ }
391
+ export interface MenuItemProps extends MenuItemBaseProps {
392
+ /** Runs the action. Call `event.preventDefault()` to keep the menu open. */
393
+ onSelect?: (event: MouseEvent<HTMLButtonElement>) => void;
394
+ }
395
+ /** An action (`menuitem`). It closes the menu; focus returns to the trigger unless the action moved it. */
396
+ export declare const MenuItem: import("react").ForwardRefExoticComponent<MenuItemProps & import("react").RefAttributes<HTMLButtonElement>>;
397
+ export interface MenuRadioGroupProps<T extends string> extends Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'onChange'> {
398
+ value: T;
399
+ onValueChange: (value: NoInfer<T>) => void;
400
+ /** Accessible name of the group, when the menu holds more than one. */
401
+ label?: string;
402
+ }
403
+ /** One choice among `MenuItemRadio`s, such as a sort order. */
404
+ export declare function MenuRadioGroup<T extends string>({ value, onValueChange, label, className, children, ...props }: MenuRadioGroupProps<T>): import("react/jsx-runtime").JSX.Element;
405
+ export interface MenuItemRadioProps extends MenuItemBaseProps {
406
+ value: string;
407
+ }
408
+ /** A `menuitemradio`: the chosen one is bold with a check. Choosing closes the menu and returns focus to the trigger. */
409
+ export declare const MenuItemRadio: import("react").ForwardRefExoticComponent<MenuItemRadioProps & import("react").RefAttributes<HTMLButtonElement>>;
410
+ export interface MenuItemCheckboxProps extends MenuItemBaseProps {
411
+ checked: boolean;
412
+ onCheckedChange: (checked: boolean) => void;
413
+ /** Close after toggling. Default false, so several toggles take one opening. */
414
+ closeOnSelect?: boolean;
415
+ }
416
+ /** A `menuitemcheckbox`: toggles in place, with a check while on. */
417
+ export declare const MenuItemCheckbox: import("react").ForwardRefExoticComponent<MenuItemCheckboxProps & import("react").RefAttributes<HTMLButtonElement>>;
418
+ /** A line between groups of items. */
419
+ export declare function MenuSeparator({ className, ...props }: Omit<HTMLAttributes<HTMLDivElement>, 'role'>): import("react/jsx-runtime").JSX.Element;
420
+ /** Plain text inside the menu, such as a question before a destructive choice. Not focusable. */
421
+ export declare function MenuLabel({ className, ...props }: HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
422
+ export interface SortOption<T extends string> {
423
+ value: T;
424
+ label: string;
425
+ /** Second line in the menu. */
426
+ hint?: ReactNode;
427
+ disabled?: boolean;
428
+ }
429
+ export interface SortMenuProps<T extends string> extends Omit<MenuProps, 'label' | 'trigger' | 'children' | 'onChange'> {
430
+ /** Name of the menu, and of the trigger with the current choice ("Ordenar: A–Z"). */
431
+ label: string;
432
+ value: T;
433
+ options: readonly SortOption<T>[];
434
+ onChange: (value: NoInfer<T>) => void;
435
+ /** Marks the choice as a filter in effect (the ring of an applied filter). */
436
+ applied?: boolean;
437
+ /** Trigger size, as `Chip`. Default `sm` (HCUI's desktop sort trigger). */
438
+ size?: ChipProps['size'];
439
+ /** Class of the trigger `Chip`. */
440
+ triggerClassName?: string;
441
+ }
442
+ /**
443
+ * Sort control: a `Chip` showing the current choice that opens a `Menu` with a
444
+ * `MenuRadioGroup` (HCUI's `DesktopSortMenu`). Options and value come from the caller.
445
+ * On phones, compose `Sheet` with `SheetOptions` behind an `IconButton` instead.
446
+ */
447
+ export declare function SortMenu<T extends string>({ label, value, options, onChange, applied, size, triggerClassName, ...props }: SortMenuProps<T>): import("react/jsx-runtime").JSX.Element;
448
+ export interface PopoverProps extends PopupBaseProps, Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'children'> {
449
+ /** Accessible name of the dialog (`aria-label`). */
450
+ label: string;
451
+ /** The button that opens it. It gets `aria-haspopup="dialog"`, `aria-expanded`, `aria-controls` and the click handler. */
452
+ trigger: PopupTrigger;
453
+ /** Free content; a function receives `close` (focus goes back to the trigger). */
454
+ children: ReactNode | ((api: {
455
+ close: () => void;
456
+ }) => ReactNode);
457
+ /** Move focus to the first focusable element inside when it opens. Default false: focus stays on the trigger. */
458
+ autoFocus?: boolean;
459
+ }
460
+ /**
461
+ * Non-modal dialog anchored to its trigger, with the same placement and dismissal as
462
+ * `Menu`: Esc closes and returns focus to the trigger, a press outside closes. Tab is
463
+ * not trapped. The caller supplies the content and its texts. Esc is taken before it
464
+ * reaches the content (so an enclosing Dialog stays open): a field inside cannot use it.
465
+ */
466
+ export declare const Popover: import("react").ForwardRefExoticComponent<PopoverProps & import("react").RefAttributes<HTMLDivElement>>;
467
+ export type TableSortDirection = 'ascending' | 'descending';
468
+ export interface TableColumn<Row> {
469
+ /** Stable id of the column; it is what `sort.key` and `onSort` name. */
470
+ key: string;
471
+ /** Header text (or node). For a sortable column it is the name of the sort button. */
472
+ header: ReactNode;
473
+ /** What the column shows for a row: free content (an avatar and a name, a formatted number). */
474
+ cell: (row: Row, index: number) => ReactNode;
475
+ /** `start` (default), `center` or `end`. Numeric columns default to `end`. */
476
+ align?: 'start' | 'center' | 'end';
477
+ /** Figures line up: tabular numbers, aligned to the end unless `align` says otherwise. */
478
+ numeric?: boolean;
479
+ /** Column width (a number is px), set on the header cell. Cells are border-box. */
480
+ width?: number | string;
481
+ /** The cell names its row: rendered as `<th scope="row">`. With `onRowClick`, its content goes in the row button. */
482
+ rowHeader?: boolean;
483
+ /** The header becomes a button that calls `onSort(key)`, and the cell gets `aria-sort`. */
484
+ sortable?: boolean;
485
+ /** Text color of the header and the cells: `muted` (a rank) or `secondary` (a category). */
486
+ tone?: 'muted' | 'secondary';
487
+ /** Class for the header and every cell of the column (e.g. to hide it in a narrow container). */
488
+ className?: string;
489
+ }
490
+ export interface TableProps<Row> extends Omit<HTMLAttributes<HTMLTableElement>, 'children' | 'onClick'> {
491
+ columns: readonly TableColumn<Row>[];
492
+ rows: readonly Row[];
493
+ /** Stable key of a row. */
494
+ rowKey: (row: Row, index: number) => string;
495
+ /** The column the rows are sorted by, and in which direction. The order of `rows` is the caller's. */
496
+ sort?: {
497
+ key: string;
498
+ direction: TableSortDirection;
499
+ } | null;
500
+ /** A sortable header was pressed. The caller decides the new order (toggle, default direction). */
501
+ onSort?: (key: string) => void;
502
+ /** The header row sticks to the top of the scrolling ancestor, at `--hc-table-sticky-top` (0 by default). */
503
+ stickyHeader?: boolean;
504
+ /** Rows react to a press, and the `rowHeader` cell becomes a button for the keyboard. */
505
+ onRowClick?: (row: Row, index: number) => void;
506
+ /** Key of the selected row (the one open in a side panel): filled and marked `aria-current` on its row button. */
507
+ selectedKey?: string | null;
508
+ }
509
+ /**
510
+ * Dense data table: a semantic `<table>` whose columns come from props. Presentation only:
511
+ * the rows, their order and what a press does are the caller's. Sortable headers are buttons
512
+ * and carry `aria-sort`; `onSort` reports the press.
513
+ */
514
+ export declare const Table: <Row>(props: TableProps<Row> & {
515
+ ref?: Ref<HTMLTableElement>;
516
+ }) => ReactElement;
517
+ type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
518
+ export interface PageHeaderProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
519
+ /** Page title. */
520
+ title: ReactNode;
521
+ /** Heading level of the title: 1 (default), 2 or 3. The look does not change. */
522
+ level?: HeadingLevel;
523
+ /** Muted number after the title, inside the heading (e.g. how many teams). Hidden when 0, null or false. */
524
+ count?: ReactNode;
525
+ /** At the end of the line: buttons, a menu. */
526
+ actions?: ReactNode;
527
+ /** Keeps the heading for assistive technology only, where the page shows its own title. */
528
+ visuallyHidden?: boolean;
529
+ }
530
+ /** Desktop page header: one line with the title (22px, semibold) and actions at the end. */
531
+ export declare const PageHeader: import("react").ForwardRefExoticComponent<PageHeaderProps & import("react").RefAttributes<HTMLElement>>;
532
+ export interface PanelHeaderStep {
533
+ label: string;
534
+ /** Goes back to this step. The last step is the current one and is never a button. */
535
+ onSelect?: () => void;
536
+ }
537
+ export interface PanelHeaderProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
538
+ /** Panel or section title. With `trail`, the last step is the title instead. */
539
+ title?: ReactNode;
540
+ /** Heading level of the title: 2 by default. The look does not change. */
541
+ level?: HeadingLevel;
542
+ /**
543
+ * `bar` (default): the header bar of a side panel (52px, a line under it, 13px text).
544
+ * `section`: a section title (17px, HCUI's `section-title`), with no bar.
545
+ */
546
+ variant?: 'bar' | 'section';
547
+ /** Small uppercase line above the title (HCUI's `eyebrow`). */
548
+ eyebrow?: ReactNode;
549
+ /** Muted line under the title. */
550
+ subtitle?: ReactNode;
551
+ /** The subtitle follows the title on the same line (baseline), cut with an ellipsis, instead of under it. */
552
+ subtitleInline?: boolean;
553
+ /** Decorative icon before the title, in a 36px muted circle (`section`); pass it sized (18px in HCUI). */
554
+ icon?: ReactNode;
555
+ /** Steps of a trail (breadcrumb): earlier steps go back with `onSelect`; the last is the title (`aria-current="location"`). */
556
+ trail?: readonly PanelHeaderStep[];
557
+ /** Accessible name of the trail's `nav` (e.g. "Trilha do painel"). */
558
+ trailLabel?: string;
559
+ /** At the end of the line, before the close button. */
560
+ actions?: ReactNode;
561
+ /** Shows a close button (an inline X, or `closeIcon`) that calls it. */
562
+ onClose?: () => void;
563
+ /** Accessible name of the close button; give it with `onClose` (e.g. "Fechar painel"). */
564
+ closeLabel?: string;
565
+ /** Tooltip of the close button, when it says more than the name (e.g. "Fechar painel (Esc)"). */
566
+ closeTitle?: string;
567
+ closeIcon?: ReactNode;
568
+ }
569
+ /** Header of a panel or section: title (or a trail), optional eyebrow, subtitle, icon, actions and close button. */
570
+ export declare const PanelHeader: import("react").ForwardRefExoticComponent<PanelHeaderProps & import("react").RefAttributes<HTMLElement>>;
571
+ export type ListRowProps = ButtonHTMLAttributes<HTMLButtonElement>;
572
+ /**
573
+ * A tappable list row (HCUI's `list-row` mixin): full width, at least 56px, 8px 16px of padding,
574
+ * a 12px gap between its children, a translucent veil on hover and press and the focus ring inside.
575
+ * The content and its semantics (`aria-pressed`, `role="option"`) are the caller's.
576
+ */
577
+ export declare const ListRow: import("react").ForwardRefExoticComponent<ListRowProps & import("react").RefAttributes<HTMLButtonElement>>;
578
+ export interface CommandPaletteItem {
579
+ /** Unique among all items. */
580
+ key: string;
581
+ label: ReactNode;
582
+ /** Second, muted line (code, points, a description), cut with an ellipsis. */
583
+ detail?: ReactNode;
584
+ /** Slot in a 32px box before the text: an avatar, an icon, a swatch. */
585
+ icon?: ReactNode;
586
+ /** Enter or a click. The palette does not close by itself: close it here when it should. */
587
+ onSelect: () => void;
588
+ /** ⌘Enter / Ctrl+Enter (HCUI: add the unit to the team). Without it, ⌘Enter selects. */
589
+ onAltSelect?: () => void;
590
+ /** Replaces the palette's `altHint` for this item. */
591
+ altHint?: ReactNode;
592
+ }
593
+ export interface CommandPaletteGroup {
594
+ key: string;
595
+ /** Small uppercase title above the group; it names the group. Without it the items follow the previous group. */
596
+ heading?: ReactNode;
597
+ items: readonly CommandPaletteItem[];
598
+ }
599
+ export interface CommandPaletteHint {
600
+ /** Each key is drawn in its own `<kbd>`. */
601
+ keys: readonly ReactNode[];
602
+ label: ReactNode;
603
+ }
604
+ export interface CommandPaletteProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'open' | 'onClose' | 'onCancel' | 'children' | 'onChange'> {
605
+ /** Controlled: shown while true. Esc and a backdrop click call `onClose`. */
606
+ open: boolean;
607
+ onClose: () => void;
608
+ /** Accessible name of the dialog and of the list. */
609
+ label: string;
610
+ /** Accessible name of the search field; `label` by default. */
611
+ inputLabel?: string;
612
+ placeholder?: string;
613
+ /** The search text (controlled). A new query makes the first item active. */
614
+ query: string;
615
+ onQueryChange: (query: string) => void;
616
+ /** Results already searched, filtered and capped by the caller, in order. */
617
+ groups: readonly CommandPaletteGroup[];
618
+ /** Icon at the start of the field; Iconoir's Search (20px) by default. */
619
+ searchIcon?: ReactNode;
620
+ /** While searching: this text shows at the end of the field and the list is `aria-busy`. */
621
+ loading?: ReactNode;
622
+ /** Message at the top of the list (nothing found); the caller decides when. */
623
+ empty?: ReactNode;
624
+ /** Key shown on the active item (HCUI: ↵). */
625
+ selectHint?: ReactNode;
626
+ /** Shown on the active item when it has `onAltSelect` (HCUI: ⌘↵ add). */
627
+ altHint?: ReactNode;
628
+ /** Keyboard hints in the footer; no footer without them. */
629
+ hints?: readonly CommandPaletteHint[];
630
+ /** Class for the scrolling list (HCUI reserves its scrollbar gutter there). */
631
+ listClassName?: string;
632
+ }
633
+ /**
634
+ * HCUI's desktop ⌘K palette: a modal `<dialog>` (`showModal()`, focus in the field,
635
+ * Esc and the backdrop ask to close, focus returns, page scroll locked) with a search
636
+ * field that is a combobox over a listbox of groups. Focus stays in the field; arrows
637
+ * (wrapping), Home and End move the active item (`aria-activedescendant`), which scrolls
638
+ * into view; Enter selects, ⌘/Ctrl+Enter takes the alternative; the pointer activates the
639
+ * item under it. Texts, results and actions come from the caller.
640
+ */
641
+ export declare const CommandPalette: import("react").ForwardRefExoticComponent<CommandPaletteProps & import("react").RefAttributes<HTMLDialogElement>>;
642
+ export {};