@orvain/utils 0.0.0-stage → 0.1.1

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 (51) hide show
  1. package/LICENSE +64 -0
  2. package/README.md +48 -2
  3. package/dist/a11y.d.cts +102 -0
  4. package/dist/a11y.d.ts +103 -0
  5. package/dist/breakpoints.d.cts +36 -0
  6. package/dist/breakpoints.d.ts +37 -0
  7. package/dist/calendar.d.cts +127 -0
  8. package/dist/calendar.d.ts +128 -0
  9. package/dist/chart/color.d.cts +72 -0
  10. package/dist/chart/color.d.ts +73 -0
  11. package/dist/chart/index.d.cts +2 -0
  12. package/dist/chart/index.d.ts +3 -0
  13. package/dist/chart/math.d.cts +66 -0
  14. package/dist/chart/math.d.ts +67 -0
  15. package/dist/chart-items/index.d.cts +64 -0
  16. package/dist/chart-items/index.d.ts +65 -0
  17. package/dist/codeTokenizer.d.cts +29 -0
  18. package/dist/codeTokenizer.d.ts +30 -0
  19. package/dist/cssModule.d.cts +12 -0
  20. package/dist/cssModule.d.ts +13 -0
  21. package/dist/dismiss.d.cts +40 -0
  22. package/dist/dismiss.d.ts +41 -0
  23. package/dist/floating.d.cts +137 -0
  24. package/dist/floating.d.ts +138 -0
  25. package/dist/formField.d.cts +58 -0
  26. package/dist/formField.d.ts +59 -0
  27. package/dist/helpers.d.cts +17 -0
  28. package/dist/helpers.d.ts +18 -0
  29. package/dist/index.cjs +11 -0
  30. package/dist/index.d.cts +17 -0
  31. package/dist/index.d.ts +18 -0
  32. package/dist/index.js +2819 -0
  33. package/dist/kanbanDnd.d.cts +71 -0
  34. package/dist/kanbanDnd.d.ts +72 -0
  35. package/dist/listNavigation.d.cts +36 -0
  36. package/dist/listNavigation.d.ts +37 -0
  37. package/dist/richText/blockquote.d.cts +11 -0
  38. package/dist/richText/blockquote.d.ts +12 -0
  39. package/dist/richText/editor.d.cts +117 -0
  40. package/dist/richText/editor.d.ts +118 -0
  41. package/dist/richText/index.d.cts +3 -0
  42. package/dist/richText/index.d.ts +4 -0
  43. package/dist/richText/model.d.cts +161 -0
  44. package/dist/richText/model.d.ts +162 -0
  45. package/dist/slider.d.cts +18 -0
  46. package/dist/slider.d.ts +19 -0
  47. package/dist/tree.d.cts +78 -0
  48. package/dist/tree.d.ts +79 -0
  49. package/dist/types.d.cts +22 -0
  50. package/dist/types.d.ts +23 -0
  51. package/package.json +56 -3
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Positioning for floating surfaces (menus, listboxes, popovers, tooltips):
3
+ * one engine for every component, framework-free. The React hook and the Vue
4
+ * composable (`internal/useFloating`) wrap it.
5
+ *
6
+ * The panel is promoted to the browser's top layer (`popover="manual"`), so no
7
+ * `overflow: hidden`, transformed or lower-stacked ancestor can clip or cover
8
+ * it, and it is placed with `position: fixed` viewport coordinates. It stays a
9
+ * DOM child of its component, which keeps Tab order, inherited styles (theme,
10
+ * `dir`, custom properties), event bubbling and light-dismiss containment
11
+ * exactly as they were inline. Without the Popover API it falls back to
12
+ * `position: fixed` on the `--orvain-z-index-*` scale.
13
+ *
14
+ * `computeFloatingLayout` is the pure geometry (placement, offset, flip,
15
+ * shift, arrow, RTL); `placeFloating` measures the DOM and applies it;
16
+ * `autoUpdate` re-places on scroll (any scroll parent), resize and size changes.
17
+ */
18
+ export type FloatingSide = 'top' | 'right' | 'bottom' | 'left';
19
+ /** Logical alignment along the anchor edge: `start` is the left edge in LTR
20
+ * and the right edge in RTL on the top / bottom sides; the top edge on the
21
+ * left / right sides. */
22
+ export type FloatingAlign = 'start' | 'center' | 'end';
23
+ export interface FloatingRect {
24
+ left: number;
25
+ top: number;
26
+ width: number;
27
+ height: number;
28
+ }
29
+ export interface FloatingLayoutOptions {
30
+ /** Side of the anchor the panel opens on. Default `bottom`. */
31
+ side?: FloatingSide | undefined;
32
+ /** Edge of the anchor the panel lines up with. Default `start`. */
33
+ align?: FloatingAlign | undefined;
34
+ /** Gap between the anchor and the panel (px). Default 0. */
35
+ offset?: number | undefined;
36
+ /** Extra move along the anchor edge (px, towards the right / bottom). Default 0. */
37
+ crossOffset?: number | undefined;
38
+ /**
39
+ * Try other sides when the panel doesn't fit on `side`. `true` (default)
40
+ * tries the opposite side, and for left / right then below and above; an
41
+ * array lists the fallbacks in order. When no side fits, the one with the
42
+ * most room wins.
43
+ */
44
+ flip?: boolean | readonly FloatingSide[] | undefined;
45
+ /** On the top / bottom sides, swap to the alignment that overflows least
46
+ * when the requested one runs past the viewport. Default false. */
47
+ flipAlign?: boolean | undefined;
48
+ /**
49
+ * Shift the panel back inside the boundary: `true` (default) along the
50
+ * anchor edge only, `'both'` also on the main axis (a panel taller than the
51
+ * room left may then cover its anchor, as a context menu does), `false` never.
52
+ */
53
+ shift?: boolean | 'both' | undefined;
54
+ /** Distance kept from the boundary edges (px). Default 8. */
55
+ padding?: number | undefined;
56
+ /** Right-to-left: swaps `start` / `end` on the top / bottom sides. */
57
+ rtl?: boolean | undefined;
58
+ /**
59
+ * With `start` / `end` alignment, line this point of the panel (px in from
60
+ * its aligned edge) up with the anchor's centre instead of aligning the
61
+ * edges, e.g. the tip of a tooltip tail.
62
+ */
63
+ anchorPoint?: number | undefined;
64
+ /** Keep the arrow centre at least this far from the panel's ends (px).
65
+ * When set, the layout reports where the arrow points at the anchor. */
66
+ arrowInset?: number | undefined;
67
+ }
68
+ export interface FloatingLayout {
69
+ /** Panel top-left, viewport coordinates. */
70
+ x: number;
71
+ y: number;
72
+ /** Resolved side and alignment (after flip / flipAlign). */
73
+ side: FloatingSide;
74
+ align: FloatingAlign;
75
+ /** The side differs from the requested one. */
76
+ flipped: boolean;
77
+ /** The shift moved the panel along the anchor edge. */
78
+ shifted: boolean;
79
+ /** Arrow centre from the panel's left (top / bottom) or top (left / right)
80
+ * edge, pointing at the anchor centre; null without `arrowInset`. */
81
+ arrow: number | null;
82
+ }
83
+ /** Pure placement maths. All rects are viewport coordinates. */
84
+ export declare function computeFloatingLayout(anchor: FloatingRect, floating: {
85
+ width: number;
86
+ height: number;
87
+ }, boundary: FloatingRect, options?: FloatingLayoutOptions): FloatingLayout;
88
+ /** A point or box to anchor to that isn't an element (a context-menu click). */
89
+ export interface VirtualAnchor {
90
+ getBoundingClientRect(): FloatingRect;
91
+ /** Element whose direction and scroll parents apply. */
92
+ contextElement?: Element | null;
93
+ }
94
+ export type FloatingAnchor = Element | VirtualAnchor;
95
+ /** A side, or one named by the reading direction: `inline-end` is the right
96
+ * side in LTR and the left side in RTL (a submenu next to its menu). */
97
+ export type FloatingLogicalSide = FloatingSide | 'inline-start' | 'inline-end';
98
+ export interface PlaceFloatingOptions extends Omit<FloatingLayoutOptions, 'side' | 'flip'> {
99
+ side?: FloatingLogicalSide | undefined;
100
+ flip?: boolean | readonly FloatingLogicalSide[] | undefined;
101
+ /** What the panel must stay inside: the viewport (default), or the part of
102
+ * it the anchor's clipping (scroll / overflow) ancestors leave visible. */
103
+ boundary?: 'viewport' | 'clippingAncestors' | undefined;
104
+ }
105
+ /** Ancestors that clip their content (overflow other than visible). */
106
+ export declare function getClippingAncestors(el: Element | null): Element[];
107
+ /**
108
+ * Measures the anchor and the panel and moves the panel: inline `left` / `top`,
109
+ * `--ods-anchor-width` (the anchor's width, for panels as wide as their
110
+ * trigger) and `data-side` for CSS that follows a flip (transform origins,
111
+ * enter direction). The panel's sizes are its layout sizes (offsetWidth /
112
+ * offsetHeight), so an entrance scale animation doesn't skew them.
113
+ */
114
+ export declare function placeFloating(anchor: FloatingAnchor, floating: HTMLElement, options?: PlaceFloatingOptions): FloatingLayout;
115
+ /**
116
+ * Re-places every open floating panel now. For an anchor that moved without
117
+ * a scroll, resize or size change (e.g. an OnboardingTour spotlight box that
118
+ * follows its target), which `autoUpdate` cannot see on its own.
119
+ */
120
+ export declare function requestFloatingUpdate(): void;
121
+ /**
122
+ * Calls `update` now and whenever the layout may have moved the anchor or
123
+ * resized either element: any scroll (captured, so every scroll parent
124
+ * counts), window resize, element resize, the web fonts loading and
125
+ * `requestFloatingUpdate()`. Returns the cleanup.
126
+ */
127
+ export declare function autoUpdate(anchor: FloatingAnchor, floating: HTMLElement, update: () => void): () => void;
128
+ /** True when the element is shown in the top layer. */
129
+ export declare function isInTopLayer(el: Element): boolean;
130
+ /**
131
+ * Promotes the panel to the top layer (`popover="manual"`: no light dismiss of
132
+ * its own; `useDismiss` handles that). Returns false where the Popover API is
133
+ * missing; the panel then stays `position: fixed` on the z-index scale.
134
+ */
135
+ export declare function showInTopLayer(el: HTMLElement): boolean;
136
+ /** Takes the panel out of the top layer again (a panel that stays mounted while closed). */
137
+ export declare function hideFromTopLayer(el: HTMLElement): void;
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Positioning for floating surfaces (menus, listboxes, popovers, tooltips):
3
+ * one engine for every component, framework-free. The React hook and the Vue
4
+ * composable (`internal/useFloating`) wrap it.
5
+ *
6
+ * The panel is promoted to the browser's top layer (`popover="manual"`), so no
7
+ * `overflow: hidden`, transformed or lower-stacked ancestor can clip or cover
8
+ * it, and it is placed with `position: fixed` viewport coordinates. It stays a
9
+ * DOM child of its component, which keeps Tab order, inherited styles (theme,
10
+ * `dir`, custom properties), event bubbling and light-dismiss containment
11
+ * exactly as they were inline. Without the Popover API it falls back to
12
+ * `position: fixed` on the `--orvain-z-index-*` scale.
13
+ *
14
+ * `computeFloatingLayout` is the pure geometry (placement, offset, flip,
15
+ * shift, arrow, RTL); `placeFloating` measures the DOM and applies it;
16
+ * `autoUpdate` re-places on scroll (any scroll parent), resize and size changes.
17
+ */
18
+ export type FloatingSide = 'top' | 'right' | 'bottom' | 'left';
19
+ /** Logical alignment along the anchor edge: `start` is the left edge in LTR
20
+ * and the right edge in RTL on the top / bottom sides; the top edge on the
21
+ * left / right sides. */
22
+ export type FloatingAlign = 'start' | 'center' | 'end';
23
+ export interface FloatingRect {
24
+ left: number;
25
+ top: number;
26
+ width: number;
27
+ height: number;
28
+ }
29
+ export interface FloatingLayoutOptions {
30
+ /** Side of the anchor the panel opens on. Default `bottom`. */
31
+ side?: FloatingSide | undefined;
32
+ /** Edge of the anchor the panel lines up with. Default `start`. */
33
+ align?: FloatingAlign | undefined;
34
+ /** Gap between the anchor and the panel (px). Default 0. */
35
+ offset?: number | undefined;
36
+ /** Extra move along the anchor edge (px, towards the right / bottom). Default 0. */
37
+ crossOffset?: number | undefined;
38
+ /**
39
+ * Try other sides when the panel doesn't fit on `side`. `true` (default)
40
+ * tries the opposite side, and for left / right then below and above; an
41
+ * array lists the fallbacks in order. When no side fits, the one with the
42
+ * most room wins.
43
+ */
44
+ flip?: boolean | readonly FloatingSide[] | undefined;
45
+ /** On the top / bottom sides, swap to the alignment that overflows least
46
+ * when the requested one runs past the viewport. Default false. */
47
+ flipAlign?: boolean | undefined;
48
+ /**
49
+ * Shift the panel back inside the boundary: `true` (default) along the
50
+ * anchor edge only, `'both'` also on the main axis (a panel taller than the
51
+ * room left may then cover its anchor, as a context menu does), `false` never.
52
+ */
53
+ shift?: boolean | 'both' | undefined;
54
+ /** Distance kept from the boundary edges (px). Default 8. */
55
+ padding?: number | undefined;
56
+ /** Right-to-left: swaps `start` / `end` on the top / bottom sides. */
57
+ rtl?: boolean | undefined;
58
+ /**
59
+ * With `start` / `end` alignment, line this point of the panel (px in from
60
+ * its aligned edge) up with the anchor's centre instead of aligning the
61
+ * edges, e.g. the tip of a tooltip tail.
62
+ */
63
+ anchorPoint?: number | undefined;
64
+ /** Keep the arrow centre at least this far from the panel's ends (px).
65
+ * When set, the layout reports where the arrow points at the anchor. */
66
+ arrowInset?: number | undefined;
67
+ }
68
+ export interface FloatingLayout {
69
+ /** Panel top-left, viewport coordinates. */
70
+ x: number;
71
+ y: number;
72
+ /** Resolved side and alignment (after flip / flipAlign). */
73
+ side: FloatingSide;
74
+ align: FloatingAlign;
75
+ /** The side differs from the requested one. */
76
+ flipped: boolean;
77
+ /** The shift moved the panel along the anchor edge. */
78
+ shifted: boolean;
79
+ /** Arrow centre from the panel's left (top / bottom) or top (left / right)
80
+ * edge, pointing at the anchor centre; null without `arrowInset`. */
81
+ arrow: number | null;
82
+ }
83
+ /** Pure placement maths. All rects are viewport coordinates. */
84
+ export declare function computeFloatingLayout(anchor: FloatingRect, floating: {
85
+ width: number;
86
+ height: number;
87
+ }, boundary: FloatingRect, options?: FloatingLayoutOptions): FloatingLayout;
88
+ /** A point or box to anchor to that isn't an element (a context-menu click). */
89
+ export interface VirtualAnchor {
90
+ getBoundingClientRect(): FloatingRect;
91
+ /** Element whose direction and scroll parents apply. */
92
+ contextElement?: Element | null;
93
+ }
94
+ export type FloatingAnchor = Element | VirtualAnchor;
95
+ /** A side, or one named by the reading direction: `inline-end` is the right
96
+ * side in LTR and the left side in RTL (a submenu next to its menu). */
97
+ export type FloatingLogicalSide = FloatingSide | 'inline-start' | 'inline-end';
98
+ export interface PlaceFloatingOptions extends Omit<FloatingLayoutOptions, 'side' | 'flip'> {
99
+ side?: FloatingLogicalSide | undefined;
100
+ flip?: boolean | readonly FloatingLogicalSide[] | undefined;
101
+ /** What the panel must stay inside: the viewport (default), or the part of
102
+ * it the anchor's clipping (scroll / overflow) ancestors leave visible. */
103
+ boundary?: 'viewport' | 'clippingAncestors' | undefined;
104
+ }
105
+ /** Ancestors that clip their content (overflow other than visible). */
106
+ export declare function getClippingAncestors(el: Element | null): Element[];
107
+ /**
108
+ * Measures the anchor and the panel and moves the panel: inline `left` / `top`,
109
+ * `--ods-anchor-width` (the anchor's width, for panels as wide as their
110
+ * trigger) and `data-side` for CSS that follows a flip (transform origins,
111
+ * enter direction). The panel's sizes are its layout sizes (offsetWidth /
112
+ * offsetHeight), so an entrance scale animation doesn't skew them.
113
+ */
114
+ export declare function placeFloating(anchor: FloatingAnchor, floating: HTMLElement, options?: PlaceFloatingOptions): FloatingLayout;
115
+ /**
116
+ * Re-places every open floating panel now. For an anchor that moved without
117
+ * a scroll, resize or size change (e.g. an OnboardingTour spotlight box that
118
+ * follows its target), which `autoUpdate` cannot see on its own.
119
+ */
120
+ export declare function requestFloatingUpdate(): void;
121
+ /**
122
+ * Calls `update` now and whenever the layout may have moved the anchor or
123
+ * resized either element: any scroll (captured, so every scroll parent
124
+ * counts), window resize, element resize, the web fonts loading and
125
+ * `requestFloatingUpdate()`. Returns the cleanup.
126
+ */
127
+ export declare function autoUpdate(anchor: FloatingAnchor, floating: HTMLElement, update: () => void): () => void;
128
+ /** True when the element is shown in the top layer. */
129
+ export declare function isInTopLayer(el: Element): boolean;
130
+ /**
131
+ * Promotes the panel to the top layer (`popover="manual"`: no light dismiss of
132
+ * its own; `useDismiss` handles that). Returns false where the Popover API is
133
+ * missing; the panel then stays `position: fixed` on the z-index scale.
134
+ */
135
+ export declare function showInTopLayer(el: HTMLElement): boolean;
136
+ /** Takes the panel out of the top layer again (a panel that stays mounted while closed). */
137
+ export declare function hideFromTopLayer(el: HTMLElement): void;
138
+ //# sourceMappingURL=floating.d.ts.map
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Native-form plumbing shared by the picker fields (Select, Combobox, TagInput,
3
+ * OtpInput, DatePickerInput…), whose value lives in component state rather
4
+ * than in a native control.
5
+ */
6
+ /**
7
+ * True when a `focusout` on `root` moves focus outside it (or nowhere), i.e.
8
+ * the whole widget lost focus. Moving between the widget's own parts (input →
9
+ * chevron, cell → cell, field → its popover) returns false.
10
+ */
11
+ export declare function focusLeftWidget(event: Pick<FocusEvent, 'relatedTarget'>, root: Element): boolean;
12
+ /**
13
+ * Reports focus leaving a whole widget, once per visit, with the `FocusEvent`
14
+ * that took it away. Three ways focus leaves without a plain focusout to an
15
+ * outside element are covered:
16
+ * - a press on a non-focusable part (an option row, a cell's padding) first
17
+ * blurs to nowhere, then the widget refocuses its control in the click
18
+ * handler: a focusout to nowhere is checked once the press is over;
19
+ * - focus to nowhere for other reasons (window switch): checked on the next
20
+ * task, and only reported if focus is really gone from the widget;
21
+ * - the focused part is removed (a popover closing on an outside press):
22
+ * browsers fire no focusout then, so while the widget has focus a
23
+ * document-level focusin / pointerdown outside it reports the leave.
24
+ *
25
+ * Bind `focusIn`, `focusOut` and `pointerDown` (capture) on the root and call
26
+ * `dispose` on unmount.
27
+ */
28
+ export declare function createFocusOut(onLeave: (event: FocusEvent) => void): {
29
+ focusIn(el: Element): void;
30
+ focusOut(event: FocusEvent, el: Element): void;
31
+ pointerDown(el: Element): void;
32
+ dispose(): void;
33
+ };
34
+ /**
35
+ * The form a widget belongs to: the one its `form` attribute names, else its
36
+ * nearest ancestor `<form>`, as for a native control.
37
+ */
38
+ export declare function ownerForm(el: Element, formId?: string | null): HTMLFormElement | null;
39
+ /**
40
+ * Listens for `reset` on the widget's form, so `form.reset()` can return an
41
+ * uncontrolled picker to its default value like a native input. Returns the
42
+ * cleanup.
43
+ */
44
+ export declare function onFormReset(el: Element, formId: string | null | undefined, handler: () => void): () => void;
45
+ /**
46
+ * The strings a picker submits: a single value always submits (an empty
47
+ * string when nothing is picked, as a native `<input>` does); a list submits
48
+ * one entry per item and nothing when empty, as `<select multiple>` does.
49
+ */
50
+ export declare function formEntries(value: string | readonly string[] | null | undefined): string[];
51
+ /** Whether a picker value counts as filled for `required`. */
52
+ export declare function formValueFilled(value: string | readonly string[] | null | undefined): boolean;
53
+ /** A date as the `yyyy-mm-dd` string an `<input type="date">` submits (local calendar day). */
54
+ export declare function toISODate(date: Date | null | undefined): string;
55
+ /** A time as the 24-hour `HH:mm` / `HH:mm:ss` string an `<input type="time">` submits. */
56
+ export declare function toISOTime(hours: number, minutes: number, seconds?: number): string;
57
+ /** Same items in the same order (a reset that changes nothing reports nothing). */
58
+ export declare function sameList<T>(a: readonly T[], b: readonly T[]): boolean;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Native-form plumbing shared by the picker fields (Select, Combobox, TagInput,
3
+ * OtpInput, DatePickerInput…), whose value lives in component state rather
4
+ * than in a native control.
5
+ */
6
+ /**
7
+ * True when a `focusout` on `root` moves focus outside it (or nowhere), i.e.
8
+ * the whole widget lost focus. Moving between the widget's own parts (input →
9
+ * chevron, cell → cell, field → its popover) returns false.
10
+ */
11
+ export declare function focusLeftWidget(event: Pick<FocusEvent, 'relatedTarget'>, root: Element): boolean;
12
+ /**
13
+ * Reports focus leaving a whole widget, once per visit, with the `FocusEvent`
14
+ * that took it away. Three ways focus leaves without a plain focusout to an
15
+ * outside element are covered:
16
+ * - a press on a non-focusable part (an option row, a cell's padding) first
17
+ * blurs to nowhere, then the widget refocuses its control in the click
18
+ * handler: a focusout to nowhere is checked once the press is over;
19
+ * - focus to nowhere for other reasons (window switch): checked on the next
20
+ * task, and only reported if focus is really gone from the widget;
21
+ * - the focused part is removed (a popover closing on an outside press):
22
+ * browsers fire no focusout then, so while the widget has focus a
23
+ * document-level focusin / pointerdown outside it reports the leave.
24
+ *
25
+ * Bind `focusIn`, `focusOut` and `pointerDown` (capture) on the root and call
26
+ * `dispose` on unmount.
27
+ */
28
+ export declare function createFocusOut(onLeave: (event: FocusEvent) => void): {
29
+ focusIn(el: Element): void;
30
+ focusOut(event: FocusEvent, el: Element): void;
31
+ pointerDown(el: Element): void;
32
+ dispose(): void;
33
+ };
34
+ /**
35
+ * The form a widget belongs to: the one its `form` attribute names, else its
36
+ * nearest ancestor `<form>`, as for a native control.
37
+ */
38
+ export declare function ownerForm(el: Element, formId?: string | null): HTMLFormElement | null;
39
+ /**
40
+ * Listens for `reset` on the widget's form, so `form.reset()` can return an
41
+ * uncontrolled picker to its default value like a native input. Returns the
42
+ * cleanup.
43
+ */
44
+ export declare function onFormReset(el: Element, formId: string | null | undefined, handler: () => void): () => void;
45
+ /**
46
+ * The strings a picker submits: a single value always submits (an empty
47
+ * string when nothing is picked, as a native `<input>` does); a list submits
48
+ * one entry per item and nothing when empty, as `<select multiple>` does.
49
+ */
50
+ export declare function formEntries(value: string | readonly string[] | null | undefined): string[];
51
+ /** Whether a picker value counts as filled for `required`. */
52
+ export declare function formValueFilled(value: string | readonly string[] | null | undefined): boolean;
53
+ /** A date as the `yyyy-mm-dd` string an `<input type="date">` submits (local calendar day). */
54
+ export declare function toISODate(date: Date | null | undefined): string;
55
+ /** A time as the 24-hour `HH:mm` / `HH:mm:ss` string an `<input type="time">` submits. */
56
+ export declare function toISOTime(hours: number, minutes: number, seconds?: number): string;
57
+ /** Same items in the same order (a reset that changes nothing reports nothing). */
58
+ export declare function sameList<T>(a: readonly T[], b: readonly T[]): boolean;
59
+ //# sourceMappingURL=formField.d.ts.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Merges class strings, filtering out falsy values.
3
+ * Thin wrapper kept here so consumers don't need to import clsx separately.
4
+ */
5
+ export declare function cx(...classes: (string | undefined | null | false)[]): string;
6
+ /**
7
+ * Type-safe Object.entries with inferred key types.
8
+ */
9
+ export declare function typedEntries<T extends object>(obj: T): [keyof T, T[keyof T]][];
10
+ /**
11
+ * Omits specified keys from an object (useful for prop forwarding).
12
+ */
13
+ export declare function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
14
+ /**
15
+ * Clamps a number between min and max (inclusive).
16
+ */
17
+ export declare function clamp(value: number, min: number, max: number): number;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Merges class strings, filtering out falsy values.
3
+ * Thin wrapper kept here so consumers don't need to import clsx separately.
4
+ */
5
+ export declare function cx(...classes: (string | undefined | null | false)[]): string;
6
+ /**
7
+ * Type-safe Object.entries with inferred key types.
8
+ */
9
+ export declare function typedEntries<T extends object>(obj: T): [keyof T, T[keyof T]][];
10
+ /**
11
+ * Omits specified keys from an object (useful for prop forwarding).
12
+ */
13
+ export declare function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
14
+ /**
15
+ * Clamps a number between min and max (inclusive).
16
+ */
17
+ export declare function clamp(value: number, min: number, max: number): number;
18
+ //# sourceMappingURL=helpers.d.ts.map