krts.ui 0.0.0-stage → 0.1.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.
@@ -0,0 +1,715 @@
1
+ import { ClassValue } from 'clsx';
2
+ import * as react from 'react';
3
+ import { ReactNode, DOMAttributes, PointerEvent, CSSProperties, HTMLAttributes, SVGProps } from 'react';
4
+ import { PressEvent, PressEvents } from 'react-aria';
5
+ import { ButtonProps as ButtonProps$1, ToggleButtonProps as ToggleButtonProps$1, ToggleButtonGroupProps, MenuProps as MenuProps$1, PopoverProps as PopoverProps$1, MenuItemProps as MenuItemProps$1, MenuSectionProps as MenuSectionProps$1, MenuTriggerProps, SubmenuTriggerProps, TooltipProps as TooltipProps$1, TooltipTriggerComponentProps, TextProps, FieldErrorProps, InputProps, LabelProps, NumberFieldProps as NumberFieldProps$1, ValidationResult, SearchFieldProps as SearchFieldProps$1, TextFieldProps as TextFieldProps$1, CheckboxProps as CheckboxProps$1, CheckboxGroupProps as CheckboxGroupProps$1, RadioProps, RadioGroupProps as RadioGroupProps$1, SwitchProps as SwitchProps$1, SliderProps as SliderProps$1, SelectProps as SelectProps$1, ListBoxItemProps, DialogProps as DialogProps$1, DialogTriggerProps, ModalOverlayProps, TabProps as TabProps$1, TabListProps, TabPanelProps, TabsProps as TabsProps$1, MeterProps as MeterProps$1, ProgressBarProps as ProgressBarProps$1, DisclosureGroupProps, DisclosureProps as DisclosureProps$1, LinkProps, SeparatorProps } from 'react-aria-components';
6
+
7
+ /** Merge class names, letting later Tailwind utilities win over earlier ones. */
8
+ declare function cn(...inputs: ClassValue[]): string;
9
+ /**
10
+ * Merge a component's base classes with a React Aria `className` prop,
11
+ * which may be a string or a render-prop function of the component state.
12
+ */
13
+ declare function composeTw<T>(className: string | ((v: T) => string) | undefined, tw: string | ((v: T) => string)): string | ((v: T) => string);
14
+ /** Focus ring shared by every interactive control. */
15
+ declare const focusRing = "outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus";
16
+
17
+ /**
18
+ * Names of the DOM CustomEvents krts.ui dispatches. They bubble, so
19
+ * non-React code can listen with `el.addEventListener('krts:doubletap', …)`.
20
+ */
21
+ declare const KRTS_EVENTS: {
22
+ readonly singleTap: "krts:singletap";
23
+ readonly doubleTap: "krts:doubletap";
24
+ readonly multiTap: "krts:multitap";
25
+ readonly holdStart: "krts:holdstart";
26
+ readonly hold: "krts:hold";
27
+ readonly holdTick: "krts:holdtick";
28
+ readonly holdEnd: "krts:holdend";
29
+ readonly holdCancel: "krts:holdcancel";
30
+ readonly swipe: "krts:swipe";
31
+ readonly confirm: "krts:confirm";
32
+ };
33
+ type KrtsEventName = (typeof KRTS_EVENTS)[keyof typeof KRTS_EVENTS];
34
+ declare function dispatchKrtsEvent<D>(target: EventTarget | null | undefined, name: KrtsEventName, detail: D): void;
35
+
36
+ /** How floating menus (Menu, Select, DropdownButton, Popover) appear. */
37
+ type OverlayAnimation = 'fade' | 'scale' | 'none';
38
+ interface KrtsConfig {
39
+ /**
40
+ * Default animation for menus, selects and popovers.
41
+ * `'fade'` (default) is a quick opacity fade — calm enough for dashboards
42
+ * where menus open dozens of times a day. `'scale'` grows the menu out of
43
+ * its trigger. `'none'` shows it instantly.
44
+ */
45
+ menuAnimation: OverlayAnimation;
46
+ }
47
+ /** Optional. Sets app-wide defaults; any component prop still wins. */
48
+ declare function KrtsProvider({ children, ...config }: Partial<KrtsConfig> & {
49
+ children: ReactNode;
50
+ }): react.JSX.Element;
51
+ declare function useKrtsConfig(): KrtsConfig;
52
+
53
+ type GesturePointerType = PressEvent['pointerType'];
54
+ interface TapGestureEvent {
55
+ type: 'singletap' | 'doubletap' | 'multitap';
56
+ /** Number of taps in the sequence (1 = single, 2 = double, …). */
57
+ count: number;
58
+ pointerType: GesturePointerType;
59
+ target: Element;
60
+ }
61
+ interface HoldGestureEvent {
62
+ type: 'holdstart' | 'hold' | 'holdtick' | 'holdend' | 'holdcancel';
63
+ /** Milliseconds since the press started. */
64
+ duration: number;
65
+ /** Whole seconds held (handy for `hold(sec)`-style logic). */
66
+ seconds: number;
67
+ /** 0 → 1 progress toward `holdDuration`. */
68
+ progress: number;
69
+ pointerType: GesturePointerType;
70
+ target: Element;
71
+ }
72
+ type SwipeDirection = 'left' | 'right' | 'up' | 'down';
73
+ interface SwipeGestureEvent {
74
+ type: 'swipe';
75
+ direction: SwipeDirection;
76
+ /** Distance travelled along the dominant axis, in px. */
77
+ distance: number;
78
+ /** px / ms along the dominant axis. */
79
+ velocity: number;
80
+ target: Element;
81
+ }
82
+ interface GestureOptions {
83
+ /** Fires once a tap is confirmed *not* to be part of a double/multi tap. */
84
+ onSingleTap?: (e: TapGestureEvent) => void;
85
+ /** Fires when two taps land within `multiTapDelay`. */
86
+ onDoubleTap?: (e: TapGestureEvent) => void;
87
+ /** Fires at the end of every tap sequence with the final `count`. */
88
+ onMultiTap?: (e: TapGestureEvent) => void;
89
+ /** Highest tap count to wait for when `onMultiTap` is used. Default 3. */
90
+ maxTaps?: number;
91
+ /** Max gap between taps in a sequence, ms. Default 250. */
92
+ multiTapDelay?: number;
93
+ /** Press began; a hold is now possible. */
94
+ onHoldStart?: (e: HoldGestureEvent) => void;
95
+ /** Held for `holdDuration` ms. A hold never also counts as a tap. */
96
+ onHold?: (e: HoldGestureEvent) => void;
97
+ /** Fires every `holdTickInterval` ms while held — e.g. at 1s, 2s, 3s. */
98
+ onHoldTick?: (e: HoldGestureEvent) => void;
99
+ /** Called every animation frame with 0 → 1 progress while held. */
100
+ onHoldProgress?: (progress: number) => void;
101
+ /** Released after `onHold` fired. */
102
+ onHoldEnd?: (e: HoldGestureEvent) => void;
103
+ /** Released (or interrupted) before `holdDuration`. */
104
+ onHoldCancel?: (e: HoldGestureEvent) => void;
105
+ /** ms before `onHold` fires. Default 500. */
106
+ holdDuration?: number;
107
+ /** ms between `onHoldTick` calls. Default 1000. */
108
+ holdTickInterval?: number;
109
+ /** Quick flick or long drag on touch/pen/mouse. */
110
+ onSwipe?: (e: SwipeGestureEvent) => void;
111
+ /** Minimum travel for a swipe, px. Default 40. */
112
+ swipeThreshold?: number;
113
+ /** A flick faster than this (px/ms) counts even under the threshold. Default 0.11. */
114
+ swipeVelocity?: number;
115
+ /** Movement (px) after which a press stops counting as a tap or hold. Default 10. */
116
+ moveTolerance?: number;
117
+ /** Also dispatch bubbling `krts:*` DOM CustomEvents. Default true. */
118
+ dispatchDomEvents?: boolean;
119
+ isDisabled?: boolean;
120
+ }
121
+ interface GestureState {
122
+ isPressed: boolean;
123
+ isHolding: boolean;
124
+ /** True once `onHold` has fired during the current press. */
125
+ didHold: boolean;
126
+ }
127
+ /**
128
+ * Low-level gesture engine. Returns React Aria press handlers (so it
129
+ * composes with any RAC component that takes `onPressStart`/`onPress`…)
130
+ * and pointer handlers for movement-based gestures (swipe, tap cancel).
131
+ */
132
+ declare function useGestureHandlers(options: GestureOptions): {
133
+ pressHandlers: Required<Pick<PressEvents, "onPress" | "onPressStart" | "onPressEnd">>;
134
+ pointerProps: {
135
+ onPointerDown: (e: PointerEvent<Element>) => void;
136
+ onPointerMove: (e: PointerEvent<Element>) => void;
137
+ onPointerUp: (e: PointerEvent<Element>) => void;
138
+ onPointerCancel: (e: PointerEvent<Element>) => void;
139
+ onContextMenu: (e: React.MouseEvent) => void;
140
+ 'data-krts-gesture': string;
141
+ };
142
+ state: GestureState;
143
+ };
144
+ interface UseGestureResult {
145
+ /** Spread onto any element (div, img, canvas…) to give it gesture events. */
146
+ gestureProps: DOMAttributes<Element> & Record<string, unknown>;
147
+ isPressed: boolean;
148
+ isHolding: boolean;
149
+ didHold: boolean;
150
+ }
151
+ /**
152
+ * Give any element `singleTap`, `doubleTap`, `multiTap`, `hold`,
153
+ * `holdTick` and `swipe` events — with keyboard (Enter/Space) and
154
+ * screen-reader support via React Aria's `usePress`.
155
+ *
156
+ * ```tsx
157
+ * const { gestureProps } = useGesture({
158
+ * onSingleTap: () => select(),
159
+ * onDoubleTap: () => like(),
160
+ * onHold: () => openMenu(),
161
+ * holdDuration: 600,
162
+ * });
163
+ * return <div {...gestureProps} tabIndex={0}>…</div>;
164
+ * ```
165
+ */
166
+ declare function useGesture(options: GestureOptions): UseGestureResult;
167
+
168
+ /**
169
+ * Honest-but-kind loading state.
170
+ *
171
+ * - Don't flash a spinner for work that finishes almost instantly
172
+ * (`delay`, default 150ms).
173
+ * - Once a spinner *is* shown, keep it up long enough to read
174
+ * (`minDuration`, default 500ms) so it never flickers.
175
+ */
176
+ declare function useSmoothPending(isPending: boolean, { delay, minDuration }?: {
177
+ delay?: number | undefined;
178
+ minDuration?: number | undefined;
179
+ }): boolean;
180
+ /**
181
+ * Progress for work whose real progress is unknown. Moves fast at first,
182
+ * then eases toward `ceiling` (never claiming to be done), and jumps to
183
+ * 100 the moment `isDone` flips. Returns a 0–100 number.
184
+ */
185
+ declare function usePerceivedProgress(isRunning: boolean, isDone: boolean, { expectedDuration, ceiling }?: {
186
+ expectedDuration?: number | undefined;
187
+ ceiling?: number | undefined;
188
+ }): number;
189
+ /** True when the user asked the OS for reduced motion. */
190
+ declare function usePrefersReducedMotion(): boolean;
191
+
192
+ interface AnimatedWidthOptions {
193
+ enabled?: boolean;
194
+ /** ms. Default 250. */
195
+ duration?: number;
196
+ }
197
+ /**
198
+ * Smoothly resizes a box when its content changes ("Save" → "Saved ✓").
199
+ *
200
+ * Attach `contentRef` to an element that hugs the content (it must not
201
+ * shrink — give it `shrink-0`). The host gets an explicit, transitioned
202
+ * `width` = content width + the host's padding and border.
203
+ *
204
+ * The first measurement (and the swap to web fonts) is applied without
205
+ * animation, so nothing slides around on page load.
206
+ */
207
+ declare function useAnimatedWidth({ enabled, duration }?: AnimatedWidthOptions): {
208
+ contentRef: react.RefObject<HTMLElement | null>;
209
+ /** Spread into the host's `style`. */
210
+ widthStyle: CSSProperties | undefined;
211
+ /** Append to the host's `transition` list. */
212
+ widthTransition: string;
213
+ };
214
+
215
+ type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'ghost';
216
+ type ButtonSize = 'xs' | 'sm' | 'md';
217
+ declare function buttonStyles({ variant, size, isIconOnly, className, }?: {
218
+ variant?: ButtonVariant;
219
+ size?: ButtonSize;
220
+ isIconOnly?: boolean;
221
+ className?: string;
222
+ }): string;
223
+ type GestureProps = Pick<GestureOptions, 'onSingleTap' | 'onDoubleTap' | 'onMultiTap' | 'maxTaps' | 'multiTapDelay' | 'onHoldStart' | 'onHold' | 'onHoldTick' | 'onHoldProgress' | 'onHoldEnd' | 'onHoldCancel' | 'holdDuration' | 'holdTickInterval' | 'onSwipe'>;
224
+ interface ButtonProps extends Omit<ButtonProps$1, 'children'>, GestureProps {
225
+ variant?: ButtonVariant;
226
+ size?: ButtonSize;
227
+ /** Square button for a lone icon. Remember an `aria-label`. */
228
+ isIconOnly?: boolean;
229
+ children?: ButtonProps$1['children'];
230
+ /** Shown before the label. */
231
+ startContent?: ReactNode;
232
+ /** Shown after the label. */
233
+ endContent?: ReactNode;
234
+ /**
235
+ * When the label changes ("Save" → "Saved"), glide to the new width with
236
+ * an ease-out curve instead of jumping. Default true (off for icon-only).
237
+ * Turn off if you size the button yourself (e.g. `w-full`).
238
+ */
239
+ animateWidth?: boolean;
240
+ }
241
+ /**
242
+ * The krts.ui button. Accepts every React Aria `Button` prop plus gesture
243
+ * events: `onSingleTap`, `onDoubleTap`, `onMultiTap`, `onHold`,
244
+ * `onHoldTick`, `onSwipe`…
245
+ *
246
+ * `isPending` shows a spinner only if the work takes longer than 150ms,
247
+ * and then keeps it for at least 500ms — no flicker.
248
+ */
249
+ declare const Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLButtonElement>>;
250
+
251
+ type HoldButtonVariant = 'danger' | 'primary' | 'secondary';
252
+ interface HoldButtonProps extends Omit<ButtonProps$1, 'children' | 'onPress' | 'onPressStart' | 'onPressEnd' | 'className' | 'style'> {
253
+ children: ReactNode;
254
+ /** Fires once the button has been held for `holdDuration`. */
255
+ onConfirm: (e: HoldGestureEvent | {
256
+ type: 'virtual';
257
+ }) => void;
258
+ /** How long to hold, ms. Default 2000. */
259
+ holdDuration?: number;
260
+ /** 0 → 1 while held (every frame). */
261
+ onHoldProgress?: (progress: number) => void;
262
+ /** Released before confirming. */
263
+ onHoldCancel?: (e: HoldGestureEvent) => void;
264
+ /** Shown briefly if someone taps instead of holding. */
265
+ hint?: ReactNode;
266
+ /** Shown after confirming. Pass `null` to keep the label. */
267
+ confirmedContent?: ReactNode;
268
+ /** How long the confirmed state is shown before resetting, ms. Default 1600. */
269
+ resetAfter?: number;
270
+ variant?: HoldButtonVariant;
271
+ size?: 'sm' | 'md';
272
+ /** Glide to the new width when the label swaps (hint / done). Default true. */
273
+ animateWidth?: boolean;
274
+ className?: string;
275
+ }
276
+ /**
277
+ * Hold-to-confirm button — a calmer alternative to an "Are you sure?"
278
+ * dialog for destructive actions.
279
+ *
280
+ * Holding fills the button left→right (linear, deliberate). Letting go
281
+ * snaps back fast (ease-out). Works with a mouse, touch, and by holding
282
+ * Space/Enter. Screen-reader activation confirms immediately, since a
283
+ * virtual click can't be held.
284
+ */
285
+ declare const HoldButton: react.ForwardRefExoticComponent<HoldButtonProps & react.RefAttributes<HTMLButtonElement>>;
286
+
287
+ interface CopyButtonProps extends Omit<ButtonProps, 'onPress' | 'children' | 'value'> {
288
+ /** Text to copy (or a function returning it). */
289
+ value: string | (() => string);
290
+ label?: string;
291
+ copiedLabel?: string;
292
+ onCopy?: (value: string) => void;
293
+ }
294
+ /**
295
+ * Copies to the clipboard and morphs its icon. The swap is a crossfade
296
+ * with a touch of blur so it reads as one object changing, not two
297
+ * objects swapping. Says "copied" immediately — the clipboard write is
298
+ * effectively instant and rarely fails.
299
+ */
300
+ declare function CopyButton({ value, label, copiedLabel, onCopy, isIconOnly, size, variant, ...props }: CopyButtonProps): react.JSX.Element;
301
+
302
+ interface GestureAreaProps extends GestureOptions, Omit<HTMLAttributes<HTMLDivElement>, keyof GestureOptions | 'onContextMenu'> {
303
+ /** Name for assistive tech. Gesture areas are focusable and act like buttons. */
304
+ 'aria-label'?: string;
305
+ }
306
+ /**
307
+ * A focusable box that understands `onSingleTap`, `onDoubleTap`,
308
+ * `onMultiTap`, `onHold`, `onHoldTick` and `onSwipe`. Exposes
309
+ * `data-pressed` / `data-holding` for styling.
310
+ *
311
+ * Keyboard: Enter/Space taps; holding Enter/Space holds.
312
+ */
313
+ declare const GestureArea: react.ForwardRefExoticComponent<GestureAreaProps & react.RefAttributes<HTMLDivElement>>;
314
+
315
+ interface SpinnerProps {
316
+ size?: number;
317
+ className?: string;
318
+ /** Accessible label. Omit when the spinner is decorative (e.g. inside a pending button). */
319
+ label?: string;
320
+ }
321
+ /**
322
+ * A fast spinner (600ms/turn). A faster spinner makes the same wait
323
+ * *feel* shorter.
324
+ */
325
+ declare function Spinner({ size, className, label }: SpinnerProps): react.JSX.Element;
326
+
327
+ interface ToggleButtonProps extends Omit<ToggleButtonProps$1, 'children'> {
328
+ children?: ReactNode;
329
+ size?: ButtonSize;
330
+ isIconOnly?: boolean;
331
+ }
332
+ /** A button that stays pressed. Selected state uses the primary fill. */
333
+ declare function ToggleButton({ size, isIconOnly, className, ...props }: ToggleButtonProps): react.JSX.Element;
334
+ interface SegmentedControlProps extends Omit<ToggleButtonGroupProps, 'children'> {
335
+ children: ReactNode;
336
+ size?: 'sm' | 'md';
337
+ }
338
+ /**
339
+ * iOS-style segmented control. Single selection by default; the thumb
340
+ * slides between segments.
341
+ */
342
+ declare function SegmentedControl({ size, className, selectionMode, disallowEmptySelection, ...props }: SegmentedControlProps): react.JSX.Element;
343
+ declare function Segment({ children, className, ...props }: Omit<ToggleButtonProps$1, 'children'> & {
344
+ children?: ReactNode;
345
+ }): react.JSX.Element;
346
+
347
+ interface DropdownButtonProps<T> extends Omit<MenuProps$1<T>, 'children' | 'className'> {
348
+ /** Button label. */
349
+ label: ReactNode;
350
+ /** Menu items (`<MenuItem>`s or a render function). */
351
+ children: MenuProps$1<T>['children'];
352
+ variant?: ButtonProps['variant'];
353
+ size?: ButtonProps['size'];
354
+ isDisabled?: boolean;
355
+ placement?: PopoverProps$1['placement'];
356
+ /** Menu animation: `'fade'` (default), `'scale'` or `'none'`. */
357
+ animation?: OverlayAnimation;
358
+ className?: string;
359
+ }
360
+ /**
361
+ * The Figma "Dropdown Button": label │ divider │ chevron, opening a menu.
362
+ * The chevron flips while the menu is open.
363
+ */
364
+ declare function DropdownButton<T extends object>({ label, children, variant, size, isDisabled, placement, className, ...menuProps }: DropdownButtonProps<T>): react.JSX.Element;
365
+ interface SplitButtonProps<T> extends DropdownButtonProps<T> {
366
+ /** Main action when the label half is pressed. */
367
+ onAction?: () => void;
368
+ onMenuAction?: MenuProps$1<T>['onAction'];
369
+ /** Accessible label for the chevron half. */
370
+ menuLabel?: string;
371
+ }
372
+ /** A main action and a menu of related actions, joined. */
373
+ declare function SplitButton<T extends object>({ label, children, variant, size, isDisabled, placement, onAction, onMenuAction, menuLabel, className, ...menuProps }: SplitButtonProps<T>): react.JSX.Element;
374
+
375
+ declare function MenuTrigger(props: MenuTriggerProps): react.JSX.Element;
376
+ declare function SubmenuTrigger(props: SubmenuTriggerProps): react.JSX.Element;
377
+ interface MenuProps<T> extends MenuProps$1<T> {
378
+ placement?: PopoverProps$1['placement'];
379
+ /** Set false when rendering a Menu inline (not in a popover). */
380
+ withPopover?: boolean;
381
+ popoverClassName?: string;
382
+ /** `'fade'` (default), `'scale'` or `'none'`. */
383
+ animation?: OverlayAnimation;
384
+ }
385
+ declare function Menu<T extends object>({ placement, withPopover, popoverClassName, animation, className, ...props }: MenuProps<T>): react.JSX.Element;
386
+ interface MenuItemProps<T> extends Omit<MenuItemProps$1<T>, 'children'> {
387
+ children?: ReactNode;
388
+ /** Secondary line under the label. */
389
+ description?: ReactNode;
390
+ /** Leading icon. */
391
+ icon?: ReactNode;
392
+ /** Keyboard shortcut shown on the right. */
393
+ shortcut?: string;
394
+ variant?: 'default' | 'danger';
395
+ }
396
+ declare function MenuItem<T extends object>({ children, description, icon, shortcut, variant, className, ...props }: MenuItemProps<T>): react.JSX.Element;
397
+ interface MenuSectionProps<T> extends MenuSectionProps$1<T> {
398
+ title?: ReactNode;
399
+ }
400
+ declare function MenuSection<T extends object>({ title, children, className, ...props }: MenuSectionProps<T>): react.JSX.Element;
401
+ declare function MenuSeparator(): react.JSX.Element;
402
+
403
+ /** Shared floating-surface look (Figma "Dropdown menu": 12px radius). */
404
+ declare const floatingSurface: string;
405
+ /**
406
+ * Enter/exit motion for floating menus. Exits are quicker than entrances.
407
+ *
408
+ * - `fade`: a quick opacity fade (default — good for dashboards).
409
+ * - `scale`: grows out of the trigger (`--trigger-anchor-point`), never from
410
+ * the centre and never from scale(0).
411
+ * - `none`: instant.
412
+ */
413
+ declare const floatingMotion: Record<OverlayAnimation, string>;
414
+ interface PopoverProps extends PopoverProps$1 {
415
+ /** Defaults to `menuAnimation` from `<KrtsProvider>`, which defaults to `'fade'`. */
416
+ animation?: OverlayAnimation;
417
+ }
418
+ declare function Popover({ className, offset, animation, ...props }: PopoverProps): react.JSX.Element;
419
+
420
+ interface TooltipProps extends Omit<TooltipProps$1, 'children'> {
421
+ children: ReactNode;
422
+ /** Small keyboard shortcut shown on the right, e.g. "⌘K". */
423
+ shortcut?: ReactNode;
424
+ showArrow?: boolean;
425
+ }
426
+ /**
427
+ * Origin-aware tooltip. React Aria handles the warm-up: the first tooltip
428
+ * waits, neighbours open instantly *and* skip their animation.
429
+ */
430
+ declare function Tooltip({ children, shortcut, showArrow, offset, className, ...props }: TooltipProps): react.JSX.Element;
431
+ interface TooltipTriggerProps extends TooltipTriggerComponentProps {
432
+ }
433
+ /** Wraps a focusable trigger and a `<Tooltip>`. Default delay 600ms. */
434
+ declare function TooltipTrigger({ delay, closeDelay, ...props }: TooltipTriggerProps): react.JSX.Element;
435
+ /** One-liner: `<WithTooltip content="Save"><Button…/></WithTooltip>` */
436
+ declare function WithTooltip({ content, children, placement, shortcut, delay, }: {
437
+ content: ReactNode;
438
+ children: React.ReactElement;
439
+ placement?: TooltipProps$1['placement'];
440
+ shortcut?: ReactNode;
441
+ delay?: number;
442
+ }): react.JSX.Element;
443
+
444
+ declare function Label(props: LabelProps): react.JSX.Element;
445
+ declare function Description(props: TextProps): react.JSX.Element;
446
+ declare function FieldError(props: FieldErrorProps): react.JSX.Element;
447
+ /** Shared look for text-like controls (from the Figma "Input Field"). */
448
+ declare const inputFrame: string;
449
+ declare const inputSizes: {
450
+ readonly sm: "h-9 text-sm";
451
+ readonly md: "h-12 text-base";
452
+ };
453
+ declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
454
+ interface FieldChrome {
455
+ label?: ReactNode;
456
+ description?: ReactNode;
457
+ errorMessage?: string | ((v: ValidationResult) => string);
458
+ size?: keyof typeof inputSizes;
459
+ }
460
+ interface TextFieldProps extends TextFieldProps$1, FieldChrome {
461
+ placeholder?: string;
462
+ /** Rendered inside the field, before the input. */
463
+ startContent?: ReactNode;
464
+ endContent?: ReactNode;
465
+ }
466
+ declare function TextField({ label, description, errorMessage, placeholder, size, startContent, endContent, className, ...props }: TextFieldProps): react.JSX.Element;
467
+ interface TextAreaFieldProps extends TextFieldProps$1, Omit<FieldChrome, 'size'> {
468
+ placeholder?: string;
469
+ rows?: number;
470
+ }
471
+ declare function TextAreaField({ label, description, errorMessage, placeholder, rows, className, ...props }: TextAreaFieldProps): react.JSX.Element;
472
+ interface SearchFieldProps extends SearchFieldProps$1, FieldChrome {
473
+ placeholder?: string;
474
+ }
475
+ declare function SearchField({ label, description, errorMessage, placeholder, size, className, ...props }: SearchFieldProps): react.JSX.Element;
476
+ interface NumberFieldProps extends NumberFieldProps$1, FieldChrome {
477
+ placeholder?: string;
478
+ }
479
+ declare function NumberField({ label, description, errorMessage, placeholder, size, className, ...props }: NumberFieldProps): react.JSX.Element;
480
+
481
+ interface CheckboxProps extends Omit<CheckboxProps$1, 'children'> {
482
+ children?: ReactNode;
483
+ }
484
+ /** 24px box from the Figma file; the check draws itself in on select. */
485
+ declare function Checkbox({ children, className, ...props }: CheckboxProps): react.JSX.Element;
486
+ interface CheckboxGroupProps extends Omit<CheckboxGroupProps$1, 'children'> {
487
+ label?: ReactNode;
488
+ description?: ReactNode;
489
+ errorMessage?: string;
490
+ children?: ReactNode;
491
+ }
492
+ declare function CheckboxGroup({ label, description, errorMessage, children, className, ...props }: CheckboxGroupProps): react.JSX.Element;
493
+
494
+ interface RadioGroupProps extends Omit<RadioGroupProps$1, 'children'> {
495
+ label?: ReactNode;
496
+ description?: ReactNode;
497
+ errorMessage?: string;
498
+ children?: ReactNode;
499
+ }
500
+ declare function RadioGroup({ label, description, errorMessage, children, className, ...props }: RadioGroupProps): react.JSX.Element;
501
+ declare function Radio({ children, className, ...props }: RadioProps): react.JSX.Element;
502
+
503
+ interface SwitchProps extends Omit<SwitchProps$1, 'children'> {
504
+ children?: ReactNode;
505
+ size?: 'sm' | 'md';
506
+ }
507
+ /**
508
+ * From the Figma "Toggle" frames — including the "halfway" frame, where
509
+ * the thumb stretches while pressed and then settles on the other side.
510
+ */
511
+ declare function Switch({ children, size, className, ...props }: SwitchProps): react.JSX.Element;
512
+
513
+ interface SliderProps<T extends number | number[]> extends SliderProps$1<T> {
514
+ label?: ReactNode;
515
+ /** Show the current value next to the label. Default true when a label is given. */
516
+ showValue?: boolean;
517
+ /** Show the filled portion of the track (Figma "Slider Fill"). Default true. */
518
+ showFill?: boolean;
519
+ /** Accessible names for each thumb (range sliders). */
520
+ thumbLabels?: string[];
521
+ }
522
+ declare function Slider<T extends number | number[]>({ label, showValue, showFill, thumbLabels, className, ...props }: SliderProps<T>): react.JSX.Element;
523
+
524
+ interface SelectProps<T extends object> extends Omit<SelectProps$1<T>, 'children'> {
525
+ label?: ReactNode;
526
+ description?: ReactNode;
527
+ errorMessage?: string;
528
+ items?: Iterable<T>;
529
+ children: ReactNode | ((item: T) => ReactNode);
530
+ size?: keyof typeof inputSizes;
531
+ /** Listbox animation: `'fade'` (default), `'scale'` or `'none'`. */
532
+ animation?: OverlayAnimation;
533
+ }
534
+ declare function Select<T extends object>({ label, description, errorMessage, children, items, size, animation, className, ...props }: SelectProps<T>): react.JSX.Element;
535
+ declare function SelectItem(props: ListBoxItemProps & {
536
+ children?: ReactNode;
537
+ }): react.JSX.Element;
538
+
539
+ declare function DialogTrigger(props: DialogTriggerProps): react.JSX.Element;
540
+ interface ModalProps extends ModalOverlayProps {
541
+ size?: 'sm' | 'md' | 'lg';
542
+ }
543
+ /** Modals stay centred (they aren't anchored to a trigger). */
544
+ declare function Modal({ size, className, children, ...props }: ModalProps): react.JSX.Element;
545
+ interface DialogProps extends Omit<DialogProps$1, 'children'> {
546
+ title?: ReactNode;
547
+ description?: ReactNode;
548
+ children?: DialogProps$1['children'];
549
+ /** Footer actions. Receives `close`. */
550
+ footer?: (close: () => void) => ReactNode;
551
+ showClose?: boolean;
552
+ }
553
+ declare function Dialog({ title, description, children, footer, showClose, className, ...props }: DialogProps): react.JSX.Element;
554
+ interface AlertDialogProps extends Omit<DialogProps, 'footer' | 'role'> {
555
+ confirmLabel?: ReactNode;
556
+ cancelLabel?: ReactNode;
557
+ variant?: 'primary' | 'danger';
558
+ onConfirm?: () => void | Promise<void>;
559
+ }
560
+ declare function AlertDialog({ confirmLabel, cancelLabel, variant, onConfirm, ...props }: AlertDialogProps): react.JSX.Element;
561
+ interface DrawerProps extends Omit<ModalOverlayProps, 'children'> {
562
+ side?: 'right' | 'left' | 'bottom';
563
+ }
564
+ /**
565
+ * A panel that slides in from an edge. Uses translate percentages so it
566
+ * works at any size, with the iOS-like drawer curve.
567
+ */
568
+ declare function Drawer({ side, className, children, ...props }: DrawerProps & {
569
+ children?: ReactNode;
570
+ }): react.JSX.Element;
571
+
572
+ type TabsVariant = 'pill' | 'underline';
573
+ interface TabsProps extends TabsProps$1 {
574
+ }
575
+ declare function Tabs({ className, ...props }: TabsProps): react.JSX.Element;
576
+ declare function TabList<T extends object>({ variant, className, ...props }: TabListProps<T> & {
577
+ variant?: TabsVariant;
578
+ }): react.JSX.Element;
579
+ interface TabProps extends Omit<TabProps$1, 'children'> {
580
+ children?: ReactNode;
581
+ }
582
+ /**
583
+ * The selection indicator glides between tabs (shared-element transition);
584
+ * text colour changes in sync with it.
585
+ */
586
+ declare function Tab({ children, className, ...props }: TabProps): react.JSX.Element;
587
+ declare function TabPanel({ className, ...props }: TabPanelProps): react.JSX.Element;
588
+
589
+ interface ProgressBarProps extends ProgressBarProps$1 {
590
+ label?: ReactNode;
591
+ showValue?: boolean;
592
+ }
593
+ /** Progress uses linear motion: it's a constant process, not a gesture. */
594
+ declare function ProgressBar({ label, showValue, className, ...props }: ProgressBarProps): react.JSX.Element;
595
+ interface MeterProps extends MeterProps$1 {
596
+ label?: ReactNode;
597
+ /** Colour the fill by thresholds (fractions of the range). */
598
+ thresholds?: {
599
+ warning: number;
600
+ danger: number;
601
+ };
602
+ }
603
+ declare function Meter({ label, thresholds, className, ...props }: MeterProps): react.JSX.Element;
604
+
605
+ interface DisclosureProps extends Omit<DisclosureProps$1, 'children'> {
606
+ title: ReactNode;
607
+ children: ReactNode;
608
+ }
609
+ /**
610
+ * Expandable section. Height animates via `--disclosure-panel-height`
611
+ * which React Aria measures for us.
612
+ */
613
+ declare function Disclosure({ title, children, className, ...props }: DisclosureProps): react.JSX.Element;
614
+ declare function Accordion({ className, ...props }: DisclosureGroupProps): react.JSX.Element;
615
+
616
+ type ToastType = 'default' | 'success' | 'error' | 'loading';
617
+ interface ToastAction {
618
+ label: ReactNode;
619
+ onPress: () => void;
620
+ }
621
+ interface ToastOptions {
622
+ id?: string | number;
623
+ description?: ReactNode;
624
+ /** ms before auto-dismiss. `Infinity` keeps it. Default 4000 (loading: Infinity). */
625
+ duration?: number;
626
+ action?: ToastAction;
627
+ onDismiss?: () => void;
628
+ }
629
+ declare function dismiss(id?: string | number): void;
630
+ /**
631
+ * Call from anywhere — no hooks or context needed. Render `<Toaster />` once.
632
+ *
633
+ * ```ts
634
+ * toast('Saved');
635
+ * toast.success('Uploaded', { description: 'photo.jpg' });
636
+ * toast('Message archived', { action: { label: 'Undo', onPress: restore } });
637
+ * toast.promise(save(), { loading: 'Saving…', success: 'Saved', error: 'Failed' });
638
+ * ```
639
+ */
640
+ declare const toast: ((title: ReactNode, opts?: ToastOptions) => string | number) & {
641
+ success: (title: ReactNode, opts?: ToastOptions) => string | number;
642
+ error: (title: ReactNode, opts?: ToastOptions) => string | number;
643
+ loading: (title: ReactNode, opts?: ToastOptions) => string | number;
644
+ dismiss: typeof dismiss;
645
+ promise<T>(promise: Promise<T>, msgs: {
646
+ loading: ReactNode;
647
+ success: ReactNode | ((v: T) => ReactNode);
648
+ error: ReactNode | ((e: unknown) => ReactNode);
649
+ }, opts?: ToastOptions): Promise<T>;
650
+ };
651
+ interface ToasterProps {
652
+ position?: 'bottom-right' | 'bottom-center' | 'bottom-left' | 'top-right' | 'top-center' | 'top-left';
653
+ /** How many toasts are visible in the collapsed stack. Default 3. */
654
+ visibleToasts?: number;
655
+ /** Always show the stack expanded. */
656
+ expand?: boolean;
657
+ }
658
+ declare function Toaster({ position, visibleToasts, expand }: ToasterProps): react.JSX.Element;
659
+
660
+ interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {
661
+ /** Render a circle (avatars). */
662
+ circle?: boolean;
663
+ }
664
+ /**
665
+ * Placeholder in the exact shape of the content to come, so the layout
666
+ * doesn't jump when it arrives.
667
+ */
668
+ declare function Skeleton({ circle, className, ...props }: SkeletonProps): react.JSX.Element;
669
+ declare const badgeTones: {
670
+ readonly neutral: "bg-surface text-fg border-line";
671
+ readonly solid: "bg-primary text-primary-fg border-primary-line";
672
+ readonly danger: "bg-danger-soft text-danger border-danger/25";
673
+ readonly success: "bg-success/10 text-success border-success/25";
674
+ readonly warning: "bg-warning/10 text-warning border-warning/25";
675
+ };
676
+ interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
677
+ tone?: keyof typeof badgeTones;
678
+ /** Show a leading status dot. */
679
+ dot?: boolean;
680
+ }
681
+ declare function Badge({ tone, dot, className, children, ...props }: BadgeProps): react.JSX.Element;
682
+ declare function Kbd({ className, ...props }: HTMLAttributes<HTMLElement>): react.JSX.Element;
683
+ interface AvatarProps extends HTMLAttributes<HTMLSpanElement> {
684
+ src?: string;
685
+ alt?: string;
686
+ /** Used for initials when there's no image. */
687
+ name?: string;
688
+ size?: number;
689
+ }
690
+ declare function Avatar({ src, alt, name, size, className, ...props }: AvatarProps): react.JSX.Element;
691
+ declare const Card: react.ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & react.RefAttributes<HTMLDivElement>>;
692
+ declare function CardHeader({ title, description, action }: {
693
+ title: ReactNode;
694
+ description?: ReactNode;
695
+ action?: ReactNode;
696
+ }): react.JSX.Element;
697
+ declare function Separator({ className, ...props }: SeparatorProps & {
698
+ className?: string;
699
+ }): react.JSX.Element;
700
+ declare function Link({ className, ...props }: LinkProps): react.JSX.Element;
701
+
702
+ type IconProps = SVGProps<SVGSVGElement> & {
703
+ size?: number;
704
+ };
705
+ declare const ChevronDownIcon: (p: IconProps) => react.JSX.Element;
706
+ declare const ChevronRightIcon: (p: IconProps) => react.JSX.Element;
707
+ declare const CheckIcon: (p: IconProps) => react.JSX.Element;
708
+ declare const MinusIcon: (p: IconProps) => react.JSX.Element;
709
+ declare const XIcon: (p: IconProps) => react.JSX.Element;
710
+ declare const SearchIcon: (p: IconProps) => react.JSX.Element;
711
+ declare const CopyIcon: (p: IconProps) => react.JSX.Element;
712
+ declare const PlusIcon: (p: IconProps) => react.JSX.Element;
713
+
714
+ export { Accordion, AlertDialog, Avatar, Badge, Button, Card, CardHeader, CheckIcon, Checkbox, CheckboxGroup, ChevronDownIcon, ChevronRightIcon, CopyButton, CopyIcon, Description, Dialog, DialogTrigger, Disclosure, Drawer, DropdownButton, FieldError, GestureArea, HoldButton, Input, KRTS_EVENTS, Kbd, KrtsProvider, Label, Link, Menu, MenuItem, MenuSection, MenuSeparator, MenuTrigger, Meter, MinusIcon, Modal, NumberField, PlusIcon, Popover, ProgressBar, Radio, RadioGroup, SearchField, SearchIcon, Segment, SegmentedControl, Select, SelectItem, Separator, Skeleton, Slider, Spinner, SplitButton, SubmenuTrigger, Switch, Tab, TabList, TabPanel, Tabs, TextAreaField, TextField, Toaster, ToggleButton, Tooltip, TooltipTrigger, WithTooltip, XIcon, buttonStyles, cn, composeTw, dispatchKrtsEvent, floatingMotion, floatingSurface, focusRing, inputFrame, toast, useAnimatedWidth, useGesture, useGestureHandlers, useKrtsConfig, usePerceivedProgress, usePrefersReducedMotion, useSmoothPending };
715
+ export type { AlertDialogProps, AnimatedWidthOptions, AvatarProps, BadgeProps, ButtonProps, ButtonSize, ButtonVariant, CheckboxGroupProps, CheckboxProps, CopyButtonProps, DialogProps, DisclosureProps, DrawerProps, DropdownButtonProps, GestureAreaProps, GestureOptions, GesturePointerType, HoldButtonProps, HoldButtonVariant, HoldGestureEvent, KrtsConfig, KrtsEventName, MenuItemProps, MenuProps, MenuSectionProps, MeterProps, ModalProps, NumberFieldProps, OverlayAnimation, PopoverProps, ProgressBarProps, RadioGroupProps, SearchFieldProps, SegmentedControlProps, SelectProps, SkeletonProps, SliderProps, SpinnerProps, SplitButtonProps, SwipeDirection, SwipeGestureEvent, SwitchProps, TabProps, TabsProps, TapGestureEvent, TextAreaFieldProps, TextFieldProps, ToastAction, ToastOptions, ToastType, ToasterProps, ToggleButtonProps, TooltipProps, TooltipTriggerProps, UseGestureResult };