@financedistrict/apps-ui 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.
Files changed (136) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +119 -0
  3. package/dist/Radio-BYWz1n7p.d.cts +32 -0
  4. package/dist/Radio-BYWz1n7p.d.ts +32 -0
  5. package/dist/SelectTrigger-By0iPBI3.d.cts +78 -0
  6. package/dist/SelectTrigger-By0iPBI3.d.ts +78 -0
  7. package/dist/accordion.cjs +322 -0
  8. package/dist/accordion.d.cts +156 -0
  9. package/dist/accordion.d.ts +156 -0
  10. package/dist/accordion.js +2 -0
  11. package/dist/apps-ui.umd.js +1 -0
  12. package/dist/button.cjs +366 -0
  13. package/dist/button.d.cts +52 -0
  14. package/dist/button.d.ts +52 -0
  15. package/dist/button.js +3 -0
  16. package/dist/checkbox.cjs +74 -0
  17. package/dist/checkbox.d.cts +63 -0
  18. package/dist/checkbox.d.ts +63 -0
  19. package/dist/checkbox.js +2 -0
  20. package/dist/chunk-3RU7JMFE.js +27 -0
  21. package/dist/chunk-5TWRK4NW.js +783 -0
  22. package/dist/chunk-7EOSDFS3.js +9 -0
  23. package/dist/chunk-AIQ4UOGK.js +142 -0
  24. package/dist/chunk-B3MMGTPV.js +200 -0
  25. package/dist/chunk-DAHQYLUI.js +146 -0
  26. package/dist/chunk-DGBXADSS.js +1 -0
  27. package/dist/chunk-E6KPNTWW.js +295 -0
  28. package/dist/chunk-EOAB6G2D.js +149 -0
  29. package/dist/chunk-FAOVKDVM.js +32 -0
  30. package/dist/chunk-FV76Z4Z2.js +78 -0
  31. package/dist/chunk-GLTLBUZ7.js +285 -0
  32. package/dist/chunk-GTZFV43T.js +154 -0
  33. package/dist/chunk-HRGZNL4A.js +88 -0
  34. package/dist/chunk-IEVN3J2F.js +98 -0
  35. package/dist/chunk-IRS3T6LF.js +352 -0
  36. package/dist/chunk-IYYHCQXG.js +146 -0
  37. package/dist/chunk-JIXW3455.js +89 -0
  38. package/dist/chunk-N4VAP2GB.js +77 -0
  39. package/dist/chunk-SJ2ZRYVV.js +167 -0
  40. package/dist/chunk-SNU4YXOR.js +66 -0
  41. package/dist/chunk-TT6JQATV.js +99 -0
  42. package/dist/chunk-UPQ3ZPCO.js +66 -0
  43. package/dist/chunk-UVUSVFOH.js +264 -0
  44. package/dist/chunk-WJFPGGJB.js +255 -0
  45. package/dist/chunk-WXEBQA7A.js +371 -0
  46. package/dist/chunk-Y2O4YYZQ.js +184 -0
  47. package/dist/cut-corner.cjs +111 -0
  48. package/dist/cut-corner.d.cts +49 -0
  49. package/dist/cut-corner.d.ts +49 -0
  50. package/dist/cut-corner.js +1 -0
  51. package/dist/dialog.cjs +441 -0
  52. package/dist/dialog.d.cts +240 -0
  53. package/dist/dialog.d.ts +240 -0
  54. package/dist/dialog.js +3 -0
  55. package/dist/feature-card.cjs +182 -0
  56. package/dist/feature-card.d.cts +54 -0
  57. package/dist/feature-card.d.ts +54 -0
  58. package/dist/feature-card.js +2 -0
  59. package/dist/filter.cjs +873 -0
  60. package/dist/filter.d.cts +129 -0
  61. package/dist/filter.d.ts +129 -0
  62. package/dist/filter.js +6 -0
  63. package/dist/index.cjs +4583 -0
  64. package/dist/index.d.cts +29 -0
  65. package/dist/index.d.ts +29 -0
  66. package/dist/index.js +27 -0
  67. package/dist/inline-select.cjs +856 -0
  68. package/dist/inline-select.d.cts +92 -0
  69. package/dist/inline-select.d.ts +92 -0
  70. package/dist/inline-select.js +6 -0
  71. package/dist/input.cjs +263 -0
  72. package/dist/input.d.cts +157 -0
  73. package/dist/input.d.ts +157 -0
  74. package/dist/input.js +4 -0
  75. package/dist/panel.cjs +185 -0
  76. package/dist/panel.d.cts +103 -0
  77. package/dist/panel.d.ts +103 -0
  78. package/dist/panel.js +2 -0
  79. package/dist/property-grid.cjs +110 -0
  80. package/dist/property-grid.d.cts +56 -0
  81. package/dist/property-grid.d.ts +56 -0
  82. package/dist/property-grid.js +2 -0
  83. package/dist/radio.cjs +74 -0
  84. package/dist/radio.d.cts +39 -0
  85. package/dist/radio.d.ts +39 -0
  86. package/dist/radio.js +2 -0
  87. package/dist/scrollbar.cjs +34 -0
  88. package/dist/scrollbar.d.cts +35 -0
  89. package/dist/scrollbar.d.ts +35 -0
  90. package/dist/scrollbar.js +1 -0
  91. package/dist/search-bar.cjs +408 -0
  92. package/dist/search-bar.d.cts +42 -0
  93. package/dist/search-bar.d.ts +42 -0
  94. package/dist/search-bar.js +4 -0
  95. package/dist/select.cjs +998 -0
  96. package/dist/select.d.cts +357 -0
  97. package/dist/select.d.ts +357 -0
  98. package/dist/select.js +5 -0
  99. package/dist/selectable-feature-card.cjs +159 -0
  100. package/dist/selectable-feature-card.d.cts +74 -0
  101. package/dist/selectable-feature-card.d.ts +74 -0
  102. package/dist/selectable-feature-card.js +3 -0
  103. package/dist/sidebar-item.cjs +175 -0
  104. package/dist/sidebar-item.d.cts +75 -0
  105. package/dist/sidebar-item.d.ts +75 -0
  106. package/dist/sidebar-item.js +2 -0
  107. package/dist/switch.cjs +86 -0
  108. package/dist/switch.d.cts +72 -0
  109. package/dist/switch.d.ts +72 -0
  110. package/dist/switch.js +2 -0
  111. package/dist/table-cell.cjs +196 -0
  112. package/dist/table-cell.d.cts +76 -0
  113. package/dist/table-cell.d.ts +76 -0
  114. package/dist/table-cell.js +2 -0
  115. package/dist/table-title.cjs +34 -0
  116. package/dist/table-title.d.cts +16 -0
  117. package/dist/table-title.d.ts +16 -0
  118. package/dist/table-title.js +2 -0
  119. package/dist/tabs.cjs +299 -0
  120. package/dist/tabs.d.cts +116 -0
  121. package/dist/tabs.d.ts +116 -0
  122. package/dist/tabs.js +2 -0
  123. package/dist/toast.cjs +87 -0
  124. package/dist/toast.d.cts +48 -0
  125. package/dist/toast.d.ts +48 -0
  126. package/dist/toast.js +2 -0
  127. package/dist/token-input.cjs +415 -0
  128. package/dist/token-input.d.cts +90 -0
  129. package/dist/token-input.d.ts +90 -0
  130. package/dist/token-input.js +3 -0
  131. package/dist/tooltip.cjs +384 -0
  132. package/dist/tooltip.d.cts +142 -0
  133. package/dist/tooltip.d.ts +142 -0
  134. package/dist/tooltip.js +2 -0
  135. package/package.json +200 -0
  136. package/src/fonts.css +33 -0
@@ -0,0 +1,408 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var react = require('@phosphor-icons/react');
5
+ var clsx = require('clsx');
6
+ var tailwindMerge = require('tailwind-merge');
7
+ var classVarianceAuthority = require('class-variance-authority');
8
+ var jsxRuntime = require('react/jsx-runtime');
9
+
10
+ function _interopNamespace(e) {
11
+ if (e && e.__esModule) return e;
12
+ var n = Object.create(null);
13
+ if (e) {
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: function () { return e[k]; }
20
+ });
21
+ }
22
+ });
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
29
+
30
+ // src/search-bar/SearchBar.tsx
31
+ function cn(...inputs) {
32
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
33
+ }
34
+ function useDebouncedCallback(fn, delayMs) {
35
+ const fnRef = React__namespace.useRef(fn);
36
+ const delayRef = React__namespace.useRef(delayMs);
37
+ fnRef.current = fn;
38
+ delayRef.current = delayMs;
39
+ const timerRef = React__namespace.useRef(null);
40
+ const debounced = React__namespace.useMemo(() => {
41
+ const clear = () => {
42
+ if (timerRef.current !== null) {
43
+ clearTimeout(timerRef.current);
44
+ timerRef.current = null;
45
+ }
46
+ };
47
+ const call = (...args) => {
48
+ clear();
49
+ timerRef.current = setTimeout(() => {
50
+ timerRef.current = null;
51
+ fnRef.current(...args);
52
+ }, delayRef.current);
53
+ };
54
+ call.cancel = clear;
55
+ call.flush = (...args) => {
56
+ clear();
57
+ fnRef.current(...args);
58
+ };
59
+ return call;
60
+ }, []);
61
+ React__namespace.useEffect(() => {
62
+ return () => {
63
+ if (timerRef.current !== null) {
64
+ clearTimeout(timerRef.current);
65
+ timerRef.current = null;
66
+ }
67
+ };
68
+ }, []);
69
+ return debounced;
70
+ }
71
+ var inputVariants = classVarianceAuthority.cva(
72
+ [
73
+ // Field row layout — icons + input on one line, vertically centred.
74
+ "flex w-full items-center",
75
+ // Colour transitions for the focus border change.
76
+ "transition-colors",
77
+ // I-beam text cursor over the WHOLE field row (not just the inner input),
78
+ // so clicking anywhere in the padding/icon area reads as "type here". This
79
+ // is the only hover affordance — hover intentionally does NOT recolour the
80
+ // border (user-authorized deviation from the Figma). `cursor-text` sits in
81
+ // the base; `has-[:disabled]:cursor-not-allowed` below is declared AFTER it
82
+ // so it wins on a disabled field (same-specificity utilities, later class
83
+ // takes precedence — confirmed by tailwind-merge ordering).
84
+ "cursor-text",
85
+ // Disabled: not-allowed cursor + block pointer interaction on the whole
86
+ // field (the native `disabled` on the input already stops typing; icons are
87
+ // dimmed separately by the `disabled`-derived `iconStateClass` in <Input>).
88
+ // Declared after `cursor-text` so it overrides the I-beam when disabled.
89
+ "has-[:disabled]:cursor-not-allowed"
90
+ ],
91
+ {
92
+ variants: {
93
+ variation: {
94
+ field: [
95
+ "bg-input-background border border-input-border rounded-sm",
96
+ // Focus (mouse or keyboard) strengthens the whole border to the
97
+ // accent. Hover does NOT change colour (cursor-only affordance).
98
+ "focus-within:border-input-border-accent"
99
+ ],
100
+ line: [
101
+ "border-b border-input-border",
102
+ // Focus (mouse or keyboard) strengthens the bottom line to the
103
+ // accent. Hover does NOT change colour (cursor-only affordance).
104
+ "focus-within:border-input-border-accent"
105
+ ]
106
+ },
107
+ size: {
108
+ // sm/lg heights reuse the Button scale; gap follows Figma per size.
109
+ sm: "h-8 gap-1",
110
+ lg: "h-10 gap-2"
111
+ },
112
+ error: {
113
+ // Error out-ranks focus: declare the destructive border for the resting
114
+ // state AND its `focus-within:` form so an errored field never recolours
115
+ // to the accent when focused (a bare `border-…` alone would lose to the
116
+ // variation's `focus-within:` accent utility above). No `hover:` form is
117
+ // needed — hover no longer changes colour on any variation. The `error`
118
+ // variant's classes are emitted after the variation's, so at equal
119
+ // specificity the destructive `focus-within:` wins by order.
120
+ true: "border-input-destructive-foreground-accent focus-within:border-input-destructive-foreground-accent",
121
+ false: ""
122
+ },
123
+ // Bottom-line visibility toggle. LINE ONLY — the hide effect is applied
124
+ // via a compound variant below (scoped to `variation:line`), so `field`
125
+ // is unaffected. Boolean → `true`/`false`; `true` (default) keeps today's
126
+ // behaviour. No classes here: `true` is the resting `border-input-border`
127
+ // from the variation, and `false`'s hide is the compound variant.
128
+ showBottomLine: {
129
+ true: "",
130
+ false: ""
131
+ }
132
+ },
133
+ compoundVariants: [
134
+ // field padding — Figma field-lg = px-3 (12px) / py-2 (8px).
135
+ { variation: "field", size: "lg", className: "px-3 py-2" },
136
+ // field-sm: DERIVED. px-2 (8px = spacing/2) steps down from field-lg's
137
+ // px-3 (12px) and equals py-2 for a uniform spacing/2 inset all around.
138
+ { variation: "field", size: "sm", className: "px-2 py-2" },
139
+ // line — no horizontal padding (Figma px-0); vertical padding per size.
140
+ { variation: "line", size: "lg", className: "py-2" },
141
+ { variation: "line", size: "sm", className: "py-2.5" },
142
+ // Hide the resting bottom line (LINE ONLY): recolour to transparent while
143
+ // keeping `border-b` for a stable 1px width (no layout shift). Gated on
144
+ // `error:false` so an errored field keeps its destructive rest border
145
+ // (transparent would otherwise last-win over it). Focus still wins:
146
+ // `focus-within:border-input-border-accent` out-ranks `border-transparent`
147
+ // (later in class order), so the line reappears on focus. `field` is
148
+ // untouched — this compound only matches `variation:line`.
149
+ {
150
+ variation: "line",
151
+ showBottomLine: false,
152
+ error: false,
153
+ className: "border-transparent"
154
+ }
155
+ ],
156
+ defaultVariants: {
157
+ variation: "field",
158
+ size: "lg",
159
+ error: false,
160
+ showBottomLine: true
161
+ }
162
+ }
163
+ );
164
+ function Input({
165
+ className,
166
+ fieldClassName,
167
+ wrapperClassName,
168
+ variation = "field",
169
+ size = "lg",
170
+ error = false,
171
+ // LINE-only bottom-line toggle. Destructured OUT of `props` so it never
172
+ // spreads onto the native <input> (invalid DOM attribute → React warning).
173
+ showBottomLine = true,
174
+ label,
175
+ optional = false,
176
+ hint,
177
+ leftSlot,
178
+ rightSlot,
179
+ disabled,
180
+ id: idProp,
181
+ ref,
182
+ ...props
183
+ }) {
184
+ const generatedId = React__namespace.useId();
185
+ const id = idProp ?? generatedId;
186
+ const hintId = hint ? `${id}-hint` : void 0;
187
+ const iconStateClass = cn(
188
+ error ? "text-input-destructive-foreground-accent" : "text-input-foreground",
189
+ disabled && "text-input-foreground-muted"
190
+ );
191
+ const HintIcon = error ? react.WarningIcon : react.InfoIcon;
192
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-1.5", wrapperClassName), children: [
193
+ label != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
194
+ /* @__PURE__ */ jsxRuntime.jsx(
195
+ "label",
196
+ {
197
+ htmlFor: id,
198
+ "data-slot": "input-label",
199
+ className: cn(
200
+ "caption-03-medium uppercase",
201
+ error ? "text-input-destructive-foreground-accent" : "text-input-foreground",
202
+ disabled && "text-input-foreground-muted"
203
+ ),
204
+ children: label
205
+ }
206
+ ),
207
+ optional && /* @__PURE__ */ jsxRuntime.jsx(
208
+ "span",
209
+ {
210
+ "data-slot": "input-optional",
211
+ className: "body-04 text-input-foreground-muted",
212
+ children: "Optional"
213
+ }
214
+ )
215
+ ] }),
216
+ /* @__PURE__ */ jsxRuntime.jsxs(
217
+ "div",
218
+ {
219
+ "data-slot": "input-field",
220
+ "data-variation": variation,
221
+ "data-error": error || void 0,
222
+ className: cn(
223
+ inputVariants({ variation, size, error, showBottomLine }),
224
+ // Icon slots inherit the shared state colour; 16px default size.
225
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
226
+ iconStateClass,
227
+ fieldClassName
228
+ ),
229
+ children: [
230
+ leftSlot != null && /* @__PURE__ */ jsxRuntime.jsx(
231
+ "span",
232
+ {
233
+ "data-slot": "input-leading",
234
+ "aria-hidden": "true",
235
+ className: "flex items-center",
236
+ children: leftSlot
237
+ }
238
+ ),
239
+ /* @__PURE__ */ jsxRuntime.jsx(
240
+ "input",
241
+ {
242
+ id,
243
+ "data-slot": "input",
244
+ disabled,
245
+ "aria-invalid": error || void 0,
246
+ "aria-describedby": hintId,
247
+ className: cn(
248
+ // Typography (Figma `body-03`) + fill the row. `min-w-0` lets the
249
+ // input shrink inside the flex row instead of overflowing.
250
+ "body-03 w-full min-w-0 bg-transparent outline-none",
251
+ // Typed text + caret colour (Figma `input-foreground-accent`).
252
+ "text-input-foreground-accent caret-input-foreground-accent",
253
+ // Placeholder colour (Figma `input-foreground-muted`).
254
+ "placeholder:text-input-foreground-muted",
255
+ // Disabled text goes muted; keep the not-allowed cursor.
256
+ "disabled:cursor-not-allowed disabled:text-input-foreground-muted",
257
+ className
258
+ ),
259
+ ref,
260
+ ...props
261
+ }
262
+ ),
263
+ rightSlot != null && // NOT aria-hidden: the trailing slot may host an interactive control
264
+ // (e.g. SearchBar's clear button) that must stay reachable by AT +
265
+ // keyboard, and a focusable element inside aria-hidden is an ARIA
266
+ // violation. Decorative bare-<svg> icons are silent to AT on their own
267
+ // (no role / no accessible name), so dropping aria-hidden here does not
268
+ // make them announced. The leading slot stays decorative (aria-hidden).
269
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-slot": "input-trailing", className: "flex items-center", children: rightSlot })
270
+ ]
271
+ }
272
+ ),
273
+ hint != null && /* @__PURE__ */ jsxRuntime.jsxs(
274
+ "p",
275
+ {
276
+ id: hintId,
277
+ "data-slot": "input-hint",
278
+ className: cn(
279
+ "body-04 flex items-center gap-1",
280
+ error ? "text-input-destructive-foreground-accent" : "text-input-foreground",
281
+ disabled && "text-input-foreground-muted"
282
+ ),
283
+ children: [
284
+ /* @__PURE__ */ jsxRuntime.jsx(
285
+ HintIcon,
286
+ {
287
+ "aria-hidden": "true",
288
+ weight: "regular",
289
+ className: "size-4 shrink-0"
290
+ }
291
+ ),
292
+ hint
293
+ ]
294
+ }
295
+ )
296
+ ] });
297
+ }
298
+ var DEFAULT_DEBOUNCE_MS = 300;
299
+ function SearchBar({
300
+ onSearch,
301
+ debounceMs = DEFAULT_DEBOUNCE_MS,
302
+ hint,
303
+ wrapperClassName,
304
+ fieldClassName,
305
+ className,
306
+ disabled,
307
+ value,
308
+ defaultValue,
309
+ onChange,
310
+ onKeyDown,
311
+ ref,
312
+ ...props
313
+ }) {
314
+ const isControlled = value !== void 0;
315
+ const innerRef = React__namespace.useRef(null);
316
+ const setInputRef = React__namespace.useCallback(
317
+ (node) => {
318
+ innerRef.current = node;
319
+ if (typeof ref === "function") ref(node);
320
+ else if (ref)
321
+ ref.current = node;
322
+ },
323
+ [ref]
324
+ );
325
+ const [hasValueState, setHasValueState] = React__namespace.useState(
326
+ () => String(value ?? defaultValue ?? "").length > 0
327
+ );
328
+ const hasValue = isControlled ? String(value ?? "").length > 0 : hasValueState;
329
+ const debouncedSearch = useDebouncedCallback((next) => {
330
+ onSearch?.(next);
331
+ }, debounceMs);
332
+ const handleChange = (event) => {
333
+ onChange?.(event);
334
+ if (!isControlled) setHasValueState(event.currentTarget.value.length > 0);
335
+ debouncedSearch(event.currentTarget.value);
336
+ };
337
+ const handleKeyDown = (event) => {
338
+ onKeyDown?.(event);
339
+ if (event.key === "Enter") {
340
+ debouncedSearch.flush(event.currentTarget.value);
341
+ }
342
+ };
343
+ const handleClear = () => {
344
+ const input = innerRef.current;
345
+ if (!input) return;
346
+ const setter = Object.getOwnPropertyDescriptor(
347
+ window.HTMLInputElement.prototype,
348
+ "value"
349
+ )?.set;
350
+ setter?.call(input, "");
351
+ input.dispatchEvent(new Event("input", { bubbles: true }));
352
+ if (!isControlled) setHasValueState(false);
353
+ debouncedSearch.cancel();
354
+ onSearch?.("");
355
+ input.focus();
356
+ };
357
+ const showClear = hasValue && !disabled;
358
+ return /* @__PURE__ */ jsxRuntime.jsx(
359
+ Input,
360
+ {
361
+ type: "search",
362
+ variation: "field",
363
+ size: "lg",
364
+ ref: setInputRef,
365
+ disabled,
366
+ hint,
367
+ value,
368
+ defaultValue,
369
+ onChange: handleChange,
370
+ onKeyDown: handleKeyDown,
371
+ className: cn(
372
+ // Suppress the browser's native `type=search` clear affordance
373
+ // (WebKit/Chrome render `::-webkit-search-cancel-button`) — SearchBar
374
+ // provides its own clear button, so the native one would show a second
375
+ // "X". `hidden` (display:none) is used rather than `appearance-none`:
376
+ // that pseudo-element is governed by `-webkit-appearance`, which the
377
+ // unprefixed `appearance:none` Tailwind emits does not affect.
378
+ // Caller `className` is merged after and can still override.
379
+ "[&::-webkit-search-cancel-button]:hidden",
380
+ className
381
+ ),
382
+ wrapperClassName,
383
+ fieldClassName,
384
+ leftSlot: /* @__PURE__ */ jsxRuntime.jsx(react.MagnifyingGlassIcon, { weight: "regular" }),
385
+ rightSlot: showClear ? /* @__PURE__ */ jsxRuntime.jsx(
386
+ "button",
387
+ {
388
+ type: "button",
389
+ "aria-label": "Clear search",
390
+ onClick: handleClear,
391
+ "data-slot": "search-clear",
392
+ className: cn(
393
+ // Reset native button chrome; inherit the field's icon colour.
394
+ "inline-flex cursor-pointer items-center justify-center rounded-xs bg-transparent p-0 text-current",
395
+ // Standard ADR-0010 system focus ring (this button is a momentary
396
+ // control — the ADR-0011 no-ring exception applies only to the
397
+ // text input itself, NOT to this button).
398
+ "outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-focus"
399
+ ),
400
+ children: /* @__PURE__ */ jsxRuntime.jsx(react.XIcon, { weight: "regular", "aria-hidden": "true" })
401
+ }
402
+ ) : void 0,
403
+ ...props
404
+ }
405
+ );
406
+ }
407
+
408
+ exports.SearchBar = SearchBar;
@@ -0,0 +1,42 @@
1
+ import * as React from 'react';
2
+
3
+ interface SearchBarProps extends Omit<React.ComponentProps<"input">, "size"> {
4
+ /**
5
+ * Fired with the current value, **debounced** by `debounceMs` (default 300).
6
+ * Runs ALONGSIDE the native `onChange` (which still fires immediately per
7
+ * keystroke — ADR-0010: the native input is the source of truth). Also fired
8
+ * immediately (debounce cancelled) on clear (`""`) and on Enter.
9
+ */
10
+ onSearch?: (value: string) => void;
11
+ /** Debounce delay for `onSearch`, in milliseconds. Default `300`. */
12
+ debounceMs?: number;
13
+ /** Helper text below the field (forwarded to Input's `hint`). */
14
+ hint?: React.ReactNode;
15
+ /** Class names for Input's outer wrapper (the vertical field/hint stack). */
16
+ wrapperClassName?: string;
17
+ /** Class names for Input's field row (border/background box), not the `<input>`. */
18
+ fieldClassName?: string;
19
+ }
20
+ /**
21
+ * SearchBar — a search field composed from `<Input>` (it renders Input; it does
22
+ * NOT fork Input's markup). Fixed to Input's `field` variation at `lg` size (no
23
+ * `label`), with a leading MagnifyingGlass icon and a trailing clear (X) button
24
+ * that appears only when there's a value.
25
+ *
26
+ * Value model (ADR-0010): the native `<input>` is the single source of truth.
27
+ * Use it uncontrolled (`defaultValue`) or controlled (`value` + `onChange`);
28
+ * `onChange` fires immediately on every keystroke and is never replaced.
29
+ * `onSearch` is a debounced convenience on top, fired ALONGSIDE `onChange`.
30
+ *
31
+ * Clear-button visibility is keyed off whether the field currently has a value.
32
+ * We track that as a minimal boolean UI flag (`hasValue`) — NOT a mirror of the
33
+ * value string: the actual text is always read from the input via `ref` (for
34
+ * `onSearch`/flush), so the native input stays the source of truth. For
35
+ * controlled use the flag is derived from the `value` prop each render; for
36
+ * uncontrolled use it seeds from `defaultValue` and updates on each input event.
37
+ *
38
+ * `ref` (a normal React 19 prop) forwards to the underlying `<input>`.
39
+ */
40
+ declare function SearchBar({ onSearch, debounceMs, hint, wrapperClassName, fieldClassName, className, disabled, value, defaultValue, onChange, onKeyDown, ref, ...props }: SearchBarProps): React.JSX.Element;
41
+
42
+ export { SearchBar, type SearchBarProps };
@@ -0,0 +1,42 @@
1
+ import * as React from 'react';
2
+
3
+ interface SearchBarProps extends Omit<React.ComponentProps<"input">, "size"> {
4
+ /**
5
+ * Fired with the current value, **debounced** by `debounceMs` (default 300).
6
+ * Runs ALONGSIDE the native `onChange` (which still fires immediately per
7
+ * keystroke — ADR-0010: the native input is the source of truth). Also fired
8
+ * immediately (debounce cancelled) on clear (`""`) and on Enter.
9
+ */
10
+ onSearch?: (value: string) => void;
11
+ /** Debounce delay for `onSearch`, in milliseconds. Default `300`. */
12
+ debounceMs?: number;
13
+ /** Helper text below the field (forwarded to Input's `hint`). */
14
+ hint?: React.ReactNode;
15
+ /** Class names for Input's outer wrapper (the vertical field/hint stack). */
16
+ wrapperClassName?: string;
17
+ /** Class names for Input's field row (border/background box), not the `<input>`. */
18
+ fieldClassName?: string;
19
+ }
20
+ /**
21
+ * SearchBar — a search field composed from `<Input>` (it renders Input; it does
22
+ * NOT fork Input's markup). Fixed to Input's `field` variation at `lg` size (no
23
+ * `label`), with a leading MagnifyingGlass icon and a trailing clear (X) button
24
+ * that appears only when there's a value.
25
+ *
26
+ * Value model (ADR-0010): the native `<input>` is the single source of truth.
27
+ * Use it uncontrolled (`defaultValue`) or controlled (`value` + `onChange`);
28
+ * `onChange` fires immediately on every keystroke and is never replaced.
29
+ * `onSearch` is a debounced convenience on top, fired ALONGSIDE `onChange`.
30
+ *
31
+ * Clear-button visibility is keyed off whether the field currently has a value.
32
+ * We track that as a minimal boolean UI flag (`hasValue`) — NOT a mirror of the
33
+ * value string: the actual text is always read from the input via `ref` (for
34
+ * `onSearch`/flush), so the native input stays the source of truth. For
35
+ * controlled use the flag is derived from the `value` prop each render; for
36
+ * uncontrolled use it seeds from `defaultValue` and updates on each input event.
37
+ *
38
+ * `ref` (a normal React 19 prop) forwards to the underlying `<input>`.
39
+ */
40
+ declare function SearchBar({ onSearch, debounceMs, hint, wrapperClassName, fieldClassName, className, disabled, value, defaultValue, onChange, onKeyDown, ref, ...props }: SearchBarProps): React.JSX.Element;
41
+
42
+ export { SearchBar, type SearchBarProps };
@@ -0,0 +1,4 @@
1
+ export { SearchBar } from './chunk-GTZFV43T.js';
2
+ import './chunk-AIQ4UOGK.js';
3
+ import './chunk-IEVN3J2F.js';
4
+ import './chunk-7EOSDFS3.js';