@knymbus/voxel-ui 1.0.11 → 1.0.14

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,457 @@
1
+ import { t as e } from "./jsx-runtime-Boo2vksn.js";
2
+ import { t } from "./Button-BgQwvn3C.js";
3
+ import { E as n, S as r } from "./icons-BpfDVwCQ.js";
4
+ import { useEffect as i, useRef as a, useState as o } from "react";
5
+ //#region src/components/search/types.ts
6
+ var s = {
7
+ colors: {
8
+ bgInput: "var(--color-vsc-bg-input, #f6f8fa)",
9
+ border: "var(--color-vsc-border, #e4e4e7)",
10
+ borderAccent: "var(--color-vsc-accent, #007acc)",
11
+ borderAccentHover: "var(--color-vsc-accent-hover, #0062a3)",
12
+ text: "var(--color-vsc-text, #333333)",
13
+ muted: "var(--color-vsc-muted, #6a737d)",
14
+ sidebarBg: "var(--color-vsc-sidebar, #f3f3f3)",
15
+ hoverBg: "var(--color-vsc-hover, #e8e8e8)",
16
+ accentBgLight: "rgba(0, 122, 204, 0.1)"
17
+ },
18
+ typography: {
19
+ sans: "var(--font-sans, font-sans, ui-sans-serif, system-ui, sans-serif)",
20
+ mono: "var(--font-mono, font-mono, ui-monospace, SFMono-Regular, monospace)"
21
+ },
22
+ transitions: { smooth: "all 200ms cubic-bezier(0.34, 1.56, 0.64, 1)" }
23
+ }, c = e();
24
+ function l({ isOpen: e, value: t, menuResults: n, onResultClick: r, onViewMoreClick: i, onCloseMenu: a }) {
25
+ let [l, u] = o(null), [d, f] = o(!1);
26
+ return !e || !t ? null : /* @__PURE__ */ (0, c.jsxs)("div", {
27
+ style: {
28
+ position: "absolute",
29
+ top: "36px",
30
+ left: 0,
31
+ width: "100%",
32
+ backgroundColor: s.colors.sidebarBg,
33
+ border: `1px solid ${s.colors.border}`,
34
+ borderRadius: "4px",
35
+ boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.2)",
36
+ zIndex: 50,
37
+ padding: "4px",
38
+ display: "flex",
39
+ flexDirection: "column",
40
+ maxHeight: "256px"
41
+ },
42
+ children: [/* @__PURE__ */ (0, c.jsx)("div", {
43
+ style: {
44
+ overflowY: "auto",
45
+ flex: 1
46
+ },
47
+ children: n.length === 0 ? /* @__PURE__ */ (0, c.jsx)("div", {
48
+ style: {
49
+ padding: "16px",
50
+ textAlign: "center",
51
+ fontSize: "11px",
52
+ color: s.colors.muted,
53
+ fontStyle: "italic"
54
+ },
55
+ children: "No indexed record entities match your text query constraints."
56
+ }) : n.map((e) => /* @__PURE__ */ (0, c.jsxs)("div", {
57
+ onMouseEnter: () => u(e.id),
58
+ onMouseLeave: () => u(null),
59
+ onClick: () => {
60
+ r && r(e), a();
61
+ },
62
+ style: {
63
+ width: "100%",
64
+ padding: "8px",
65
+ backgroundColor: l === e.id ? s.colors.hoverBg : "transparent",
66
+ borderRadius: "2px",
67
+ cursor: "pointer",
68
+ display: "flex",
69
+ flexDirection: "column",
70
+ gap: "2px",
71
+ transition: "background-color 100ms",
72
+ boxSizing: "border-box"
73
+ },
74
+ children: [/* @__PURE__ */ (0, c.jsxs)("div", {
75
+ style: {
76
+ fontWeight: 600,
77
+ color: s.colors.text,
78
+ display: "flex",
79
+ justifyContent: "space-between",
80
+ alignItems: "center",
81
+ width: "100%"
82
+ },
83
+ children: [/* @__PURE__ */ (0, c.jsx)("span", {
84
+ style: {
85
+ whiteSpace: "nowrap",
86
+ overflow: "hidden",
87
+ textOverflow: "ellipsis",
88
+ flex: 1
89
+ },
90
+ children: e.title
91
+ }), e.category && /* @__PURE__ */ (0, c.jsx)("span", {
92
+ style: {
93
+ fontSize: "9px",
94
+ fontFamily: "monospace",
95
+ fontWeight: "bold",
96
+ backgroundColor: s.colors.accentBgLight,
97
+ border: "1px solid rgba(0, 122, 204, 0.2)",
98
+ color: s.colors.borderAccent,
99
+ padding: "2px 4px",
100
+ borderRadius: "2px",
101
+ textTransform: "uppercase",
102
+ marginLeft: "8px",
103
+ flexShrink: 0
104
+ },
105
+ children: e.category
106
+ })]
107
+ }), e.subtitle && /* @__PURE__ */ (0, c.jsx)("div", {
108
+ style: {
109
+ fontSize: "10px",
110
+ color: s.colors.muted,
111
+ whiteSpace: "nowrap",
112
+ overflow: "hidden",
113
+ textOverflow: "ellipsis"
114
+ },
115
+ children: e.subtitle
116
+ })]
117
+ }, e.id))
118
+ }), n.length > 0 && i && /* @__PURE__ */ (0, c.jsx)("button", {
119
+ onMouseEnter: () => f(!0),
120
+ onMouseLeave: () => f(!1),
121
+ onClick: () => {
122
+ i(), a();
123
+ },
124
+ style: {
125
+ width: "100%",
126
+ border: "none",
127
+ borderTop: `1px solid ${s.colors.border}`,
128
+ backgroundColor: d ? s.colors.hoverBg : "transparent",
129
+ padding: "8px",
130
+ textAlign: "center",
131
+ fontSize: "10px",
132
+ fontWeight: "bold",
133
+ color: s.colors.borderAccent,
134
+ letterSpacing: "0.5px",
135
+ textTransform: "uppercase",
136
+ transition: "background-color 150ms",
137
+ borderRadius: "0 0 2px 2px",
138
+ cursor: "pointer"
139
+ },
140
+ children: "View All Matching Search Metrics Records →"
141
+ })]
142
+ });
143
+ }
144
+ //#endregion
145
+ //#region src/components/search/SearchInput.tsx
146
+ function u({ variant: e = "simple", value: u, onChange: d, onClear: f, placeholder: p = "Search manifests or tracking codes...", resultsCount: m = 0, menuResults: h = [], onResultClick: g, onViewMoreClick: _, className: v = "", showFloatPeek: y = !1, peekContent: b }) {
147
+ let [x, S] = o(!1), [C, w] = o(!1), [T, E] = o(!1), [D, O] = o(!1), k = a(null), A = a(null);
148
+ i(() => {
149
+ let t = (t) => {
150
+ k.current && !k.current.contains(t.target) && (w(!1), e === "float" && u === "" && S(!1));
151
+ };
152
+ return window.addEventListener("mousedown", t), () => window.removeEventListener("mousedown", t);
153
+ }, [e, u]);
154
+ let j = (e) => {
155
+ e.stopPropagation(), d(""), f && f(), A.current && A.current.focus();
156
+ }, M = () => {
157
+ S(!0), setTimeout(() => A.current?.focus(), 50);
158
+ }, N = {
159
+ position: "relative",
160
+ display: "flex",
161
+ flexDirection: "column",
162
+ fontFamily: s.typography.sans,
163
+ fontSize: "12px",
164
+ userSelect: "none",
165
+ boxSizing: "border-box"
166
+ }, P = {
167
+ position: "relative",
168
+ display: "flex",
169
+ alignItems: "center",
170
+ backgroundColor: s.colors.bgInput,
171
+ border: `1px solid ${D || e === "float" && x ? s.colors.borderAccent : T ? s.colors.borderAccentHover : s.colors.border}`,
172
+ borderRadius: "6px",
173
+ height: "32px",
174
+ transition: s.transitions.smooth,
175
+ boxSizing: "border-box"
176
+ }, F = {
177
+ width: "100%",
178
+ height: "100%",
179
+ backgroundColor: "transparent",
180
+ color: s.colors.text,
181
+ border: "none",
182
+ outline: "none",
183
+ padding: "0 32px",
184
+ fontSize: "12px",
185
+ fontFamily: s.typography.sans,
186
+ boxSizing: "border-box"
187
+ };
188
+ if (e === "simple") {
189
+ let e = m > 0 && u.length > 0, i = b || /* @__PURE__ */ (0, c.jsxs)("p", {
190
+ style: {
191
+ margin: 0,
192
+ fontSize: "10px",
193
+ fontFamily: s.typography.mono,
194
+ color: s.colors.muted,
195
+ whiteSpace: "nowrap",
196
+ overflow: "hidden",
197
+ textOverflow: "ellipsis"
198
+ },
199
+ children: [
200
+ "Showing ",
201
+ /* @__PURE__ */ (0, c.jsx)("span", {
202
+ style: {
203
+ color: s.colors.borderAccent,
204
+ fontWeight: "bold"
205
+ },
206
+ children: m
207
+ }),
208
+ " matching database metrics records"
209
+ ]
210
+ });
211
+ return /* @__PURE__ */ (0, c.jsxs)("div", {
212
+ style: N,
213
+ className: v,
214
+ children: [/* @__PURE__ */ (0, c.jsxs)("div", {
215
+ style: P,
216
+ onMouseEnter: () => E(!0),
217
+ onMouseLeave: () => E(!1),
218
+ children: [
219
+ /* @__PURE__ */ (0, c.jsx)("div", {
220
+ style: {
221
+ position: "absolute",
222
+ left: "10px",
223
+ display: "flex",
224
+ alignItems: "center",
225
+ color: T ? s.colors.text : s.colors.muted
226
+ },
227
+ children: /* @__PURE__ */ (0, c.jsx)(r, { size: 14 })
228
+ }),
229
+ /* @__PURE__ */ (0, c.jsx)("input", {
230
+ ref: A,
231
+ type: "text",
232
+ value: u,
233
+ onChange: (e) => d(e.target.value),
234
+ onFocus: () => O(!0),
235
+ onBlur: () => O(!1),
236
+ placeholder: p,
237
+ style: F
238
+ }),
239
+ u && /* @__PURE__ */ (0, c.jsx)("div", {
240
+ style: {
241
+ position: "absolute",
242
+ right: "4px",
243
+ top: "50%",
244
+ transform: "translateY(-50%)",
245
+ display: "flex",
246
+ alignItems: "center"
247
+ },
248
+ children: /* @__PURE__ */ (0, c.jsx)(t, {
249
+ icon: n,
250
+ color: "ghost",
251
+ iconOnly: !0,
252
+ onClick: j,
253
+ size: "xs"
254
+ })
255
+ })
256
+ ]
257
+ }), /* @__PURE__ */ (0, c.jsx)("div", {
258
+ style: {
259
+ position: "absolute",
260
+ left: 0,
261
+ right: 0,
262
+ top: "32px",
263
+ zIndex: 10,
264
+ overflow: "hidden",
265
+ transition: s.transitions.smooth,
266
+ height: e ? "24px" : "0px",
267
+ opacity: +!!e,
268
+ transform: e ? "translateY(0px)" : "translateY(-4px)",
269
+ pointerEvents: "none"
270
+ },
271
+ children: /* @__PURE__ */ (0, c.jsx)("div", {
272
+ style: {
273
+ backgroundColor: s.colors.sidebarBg,
274
+ border: `1px solid ${s.colors.border}`,
275
+ borderTop: "none",
276
+ padding: "4px 6px",
277
+ borderRadius: "0 0 6px 6px",
278
+ boxShadow: "0 4px 6px -1px rgba(0,0,0,0.1)"
279
+ },
280
+ children: i
281
+ })
282
+ })]
283
+ });
284
+ }
285
+ if (e === "float") {
286
+ let e = y && m > 0 && u.length > 0, i = x || u.length > 0, a = b || /* @__PURE__ */ (0, c.jsxs)("p", {
287
+ style: {
288
+ margin: 0,
289
+ fontSize: "9px",
290
+ fontFamily: s.typography.mono,
291
+ color: s.colors.borderAccent,
292
+ whiteSpace: "nowrap",
293
+ overflow: "hidden",
294
+ textOverflow: "ellipsis"
295
+ },
296
+ children: [
297
+ "Quick Peek: Found ",
298
+ m,
299
+ " rows"
300
+ ]
301
+ });
302
+ return /* @__PURE__ */ (0, c.jsxs)("div", {
303
+ ref: k,
304
+ style: N,
305
+ className: v,
306
+ children: [/* @__PURE__ */ (0, c.jsx)("div", {
307
+ onMouseEnter: () => E(!0),
308
+ onMouseLeave: () => E(!1),
309
+ style: {
310
+ ...P,
311
+ width: i ? "256px" : "32px",
312
+ justifyContent: i ? "flex-start" : "center",
313
+ backgroundColor: i ? s.colors.bgInput : "transparent",
314
+ borderColor: i ? s.colors.borderAccent : "transparent",
315
+ padding: i ? "0 8px" : "0"
316
+ },
317
+ children: i ? /* @__PURE__ */ (0, c.jsxs)("div", {
318
+ style: {
319
+ position: "relative",
320
+ display: "flex",
321
+ alignItems: "center",
322
+ width: "100%",
323
+ height: "100%"
324
+ },
325
+ children: [
326
+ /* @__PURE__ */ (0, c.jsx)(r, {
327
+ size: 14,
328
+ style: {
329
+ color: s.colors.text,
330
+ flexShrink: 0
331
+ }
332
+ }),
333
+ /* @__PURE__ */ (0, c.jsx)("input", {
334
+ ref: A,
335
+ type: "text",
336
+ value: u,
337
+ onChange: (e) => d(e.target.value),
338
+ placeholder: p,
339
+ style: {
340
+ ...F,
341
+ padding: "0 24px 0 8px"
342
+ }
343
+ }),
344
+ u && /* @__PURE__ */ (0, c.jsx)("div", {
345
+ style: {
346
+ position: "absolute",
347
+ right: 0,
348
+ top: "50%",
349
+ transform: "translateY(-50%)",
350
+ display: "flex",
351
+ alignItems: "center"
352
+ },
353
+ children: /* @__PURE__ */ (0, c.jsx)(t, {
354
+ iconOnly: !0,
355
+ color: "ghost",
356
+ icon: n,
357
+ onClick: j,
358
+ size: "xs"
359
+ })
360
+ })
361
+ ]
362
+ }) : /* @__PURE__ */ (0, c.jsx)(t, {
363
+ iconOnly: !0,
364
+ size: "sm",
365
+ icon: r,
366
+ onClick: M,
367
+ color: "ghost",
368
+ style: { color: s.colors.muted },
369
+ title: "Open Expandable Search"
370
+ })
371
+ }), /* @__PURE__ */ (0, c.jsx)("div", {
372
+ style: {
373
+ position: "absolute",
374
+ top: "32px",
375
+ width: "256px",
376
+ zIndex: 10,
377
+ overflow: "hidden",
378
+ transition: s.transitions.smooth,
379
+ height: e ? "24px" : "0px",
380
+ opacity: +!!e,
381
+ transform: e ? "translateY(0px)" : "translateY(-4px)",
382
+ pointerEvents: "none"
383
+ },
384
+ children: /* @__PURE__ */ (0, c.jsx)("div", {
385
+ style: {
386
+ backgroundColor: s.colors.sidebarBg,
387
+ border: `1px solid ${s.colors.border}`,
388
+ borderTop: "none",
389
+ padding: "4px",
390
+ borderRadius: "0 0 2px 2px",
391
+ boxShadow: "0 4px 6px -1px rgba(0,0,0,0.1)"
392
+ },
393
+ children: a
394
+ })
395
+ })]
396
+ });
397
+ }
398
+ return /* @__PURE__ */ (0, c.jsxs)("div", {
399
+ ref: k,
400
+ style: N,
401
+ className: v,
402
+ children: [/* @__PURE__ */ (0, c.jsxs)("div", {
403
+ style: P,
404
+ onMouseEnter: () => E(!0),
405
+ onMouseLeave: () => E(!1),
406
+ children: [
407
+ /* @__PURE__ */ (0, c.jsx)("div", {
408
+ style: {
409
+ position: "absolute",
410
+ left: "10px",
411
+ display: "flex",
412
+ alignItems: "center",
413
+ color: s.colors.muted
414
+ },
415
+ children: /* @__PURE__ */ (0, c.jsx)(r, { size: 14 })
416
+ }),
417
+ /* @__PURE__ */ (0, c.jsx)("input", {
418
+ ref: A,
419
+ type: "text",
420
+ value: u,
421
+ onChange: (e) => {
422
+ d(e.target.value), w(!0);
423
+ },
424
+ onFocus: () => w(!0),
425
+ placeholder: p,
426
+ style: F
427
+ }),
428
+ u && /* @__PURE__ */ (0, c.jsx)("div", {
429
+ style: {
430
+ position: "absolute",
431
+ right: "4px",
432
+ top: "50%",
433
+ transform: "translateY(-50%)",
434
+ display: "flex",
435
+ alignItems: "center"
436
+ },
437
+ children: /* @__PURE__ */ (0, c.jsx)(t, {
438
+ iconOnly: !0,
439
+ icon: n,
440
+ color: "ghost",
441
+ onClick: j,
442
+ size: "xs"
443
+ })
444
+ })
445
+ ]
446
+ }), /* @__PURE__ */ (0, c.jsx)(l, {
447
+ isOpen: C,
448
+ value: u,
449
+ menuResults: h,
450
+ onResultClick: g,
451
+ onViewMoreClick: _,
452
+ onCloseMenu: () => w(!1)
453
+ })]
454
+ });
455
+ }
456
+ //#endregion
457
+ export { s as n, u as t };
@@ -1,10 +1,3 @@
1
- import { default as React, ReactNode } from 'react';
1
+ import { default as React } from 'react';
2
2
  import { SearchInputProps } from './types';
3
- interface AdvancedSearchInputProps extends Omit<SearchInputProps, 'variant'> {
4
- variant?: 'simple' | 'float' | 'menu';
5
- showFloatPeek?: boolean;
6
- resultIndicatorPanel?: string | ReactNode;
7
- }
8
- export default function SearchInput({ variant, value, onChange, onClear, placeholder, resultsCount, menuResults, onResultClick, onViewMoreClick, className, showFloatPeek, // Optional feature flag default boundary
9
- resultIndicatorPanel }: AdvancedSearchInputProps): React.JSX.Element;
10
- export {};
3
+ export default function SearchInput({ variant, value, onChange, onClear, placeholder, resultsCount, menuResults, onResultClick, onViewMoreClick, className, showFloatPeek, peekContent }: SearchInputProps): React.JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { SearchResultItem } from './types';
2
+ interface SearchMenuProps {
3
+ isOpen: boolean;
4
+ value: string;
5
+ menuResults: SearchResultItem[];
6
+ onResultClick?: (item: SearchResultItem) => void;
7
+ onViewMoreClick?: () => void;
8
+ onCloseMenu: () => void;
9
+ }
10
+ export default function SearchMenu({ isOpen, value, menuResults, onResultClick, onViewMoreClick, onCloseMenu }: SearchMenuProps): import("react").JSX.Element | null;
11
+ export {};
@@ -1,2 +1,2 @@
1
- import { t as e } from "../../chunks/search-BnD-97mv.js";
2
- export { e as SearchInput };
1
+ import { n as e, t } from "../../chunks/search-CIhA-ti4.js";
2
+ export { t as SearchInput, e as searchTokens };
@@ -1,3 +1,4 @@
1
+ import { default as React } from 'react';
1
2
  export type SearchVariant = 'simple' | 'float' | 'menu';
2
3
  export interface SearchResultItem {
3
4
  id: string;
@@ -16,4 +17,26 @@ export interface SearchInputProps {
16
17
  onResultClick?: (item: SearchResultItem) => void;
17
18
  onViewMoreClick?: () => void;
18
19
  className?: string;
20
+ showFloatPeek?: boolean;
21
+ peekContent?: React.ReactNode;
19
22
  }
23
+ export declare const searchTokens: {
24
+ colors: {
25
+ bgInput: string;
26
+ border: string;
27
+ borderAccent: string;
28
+ borderAccentHover: string;
29
+ text: string;
30
+ muted: string;
31
+ sidebarBg: string;
32
+ hoverBg: string;
33
+ accentBgLight: string;
34
+ };
35
+ typography: {
36
+ sans: string;
37
+ mono: string;
38
+ };
39
+ transitions: {
40
+ smooth: string;
41
+ };
42
+ };
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
- (function(){try{if(typeof document<`u`){var e=document.createElement(`style`);e.appendChild(document.createTextNode(`/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */@layer theme{:root,:host{--voxel-font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--voxel-font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--voxel-default-font-family:var(--voxel-font-sans);--voxel-default-mono-font-family:var(--voxel-font-mono)}}@layer base{@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--voxel-default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--voxel-default-font-feature-settings,normal);font-variation-settings:var(--voxel-default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--voxel-default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--voxel-default-mono-font-feature-settings,normal);font-variation-settings:var(--voxel-default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}.dark{--color-vsc-bg:#1e1e1e;--color-vsc-sidebar:#252526;--color-vsc-border:#3c3c3c;--color-vsc-accent:#007acc;--color-vsc-accent-hover:#0062a3;--color-vsc-hover:#2a2d2e;--color-vsc-text:#ccc;--color-vsc-muted:#8b949e;--color-vsc-bg-input:#1f1f1f}}@layer components,utilities;/*$vite$:1*/`)),document.head.appendChild(e)}}catch(e){console.error(`vite-plugin-css-injected-by-js`,e)}})();
2
1
  import { t as e } from "./chunks/resizable-ImB8dfG_.js";
3
2
  import { i as t, n, r, t as i } from "./chunks/tabs-MaVN00hJ.js";
4
3
  import { t as a } from "./chunks/Button-BgQwvn3C.js";
5
4
  import { n as o, t as s } from "./chunks/button-dHcpTNIG.js";
6
5
  import { C as c, E as l, S as u, T as d, _ as f, a as p, b as m, c as h, d as g, f as _, g as v, h as y, i as b, l as x, m as S, n as C, o as w, p as T, r as E, s as D, t as O, u as k, v as A, w as j, x as M, y as N } from "./chunks/icons-BpfDVwCQ.js";
7
- import { t as P } from "./chunks/search-BnD-97mv.js";
8
- export { A as Add, S as BlankDoc, a as Button, o as ButtonGroup, _ as Chat, N as ChevronDown, l as Close, x as Comment, k as DeleteChat, j as Document, v as Expand, c as Folder, C as Group, y as Minimize, f as Minus, w as More, D as OpenFolder, b as Person, g as PlusChat, h as PlusComment, T as PlusDoc, O as PlusDocBadge, E as PlusPerson, M as Refresh, e as ResizablePanel, u as Search, P as SearchInput, s as SplitActionButton, i as TabButton, n as TabPanel, r as TabPanelList, d as Terminal, m as Trash, p as Truck, t as useTab };
6
+ import { n as P, t as F } from "./chunks/search-CIhA-ti4.js";
7
+ export { A as Add, S as BlankDoc, a as Button, o as ButtonGroup, _ as Chat, N as ChevronDown, l as Close, x as Comment, k as DeleteChat, j as Document, v as Expand, c as Folder, C as Group, y as Minimize, f as Minus, w as More, D as OpenFolder, b as Person, g as PlusChat, h as PlusComment, T as PlusDoc, O as PlusDocBadge, E as PlusPerson, M as Refresh, e as ResizablePanel, u as Search, F as SearchInput, s as SplitActionButton, i as TabButton, n as TabPanel, r as TabPanelList, d as Terminal, m as Trash, p as Truck, P as searchTokens, t as useTab };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knymbus/voxel-ui",
3
- "version": "1.0.11",
3
+ "version": "1.0.14",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -1,14 +1,8 @@
1
- import React, { useState, useRef, useEffect, ReactNode } from 'react';
2
- import { SearchInputProps } from './types';
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { SearchInputProps, searchTokens } from './types';
3
3
  import { Search, Close } from '../icons';
4
4
  import { Button } from '../button/Button';
5
-
6
- // Extend your props declaration interface locally to support the new feature flags
7
- interface AdvancedSearchInputProps extends Omit<SearchInputProps, 'variant'> {
8
- variant?: 'simple' | 'float' | 'menu';
9
- showFloatPeek?: boolean; // When true, float variant slides down a results indicator bar
10
- resultIndicatorPanel?: string | ReactNode
11
- }
5
+ import SearchMenu from './SearchMenu';
12
6
 
13
7
  export default function SearchInput({
14
8
  variant = 'simple',
@@ -21,15 +15,17 @@ export default function SearchInput({
21
15
  onResultClick,
22
16
  onViewMoreClick,
23
17
  className = '',
24
- showFloatPeek = false, // Optional feature flag default boundary
25
- resultIndicatorPanel
26
- }: AdvancedSearchInputProps) {
18
+ showFloatPeek = false,
19
+ peekContent
20
+ }: SearchInputProps) {
27
21
  const [isExpanded, setIsExpanded] = useState<boolean>(false);
28
22
  const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
23
+ const [isInputHovered, setIsInputHovered] = useState<boolean>(false);
24
+ const [isInputFocused, setIsInputFocused] = useState<boolean>(false);
25
+
29
26
  const containerRef = useRef<HTMLDivElement | null>(null);
30
27
  const inputRef = useRef<HTMLInputElement | null>(null);
31
28
 
32
- // Auto-close overlay dropdowns if the user clicks completely out of the component framework bounding box
33
29
  useEffect(() => {
34
30
  const handleOutsideClick = (e: MouseEvent) => {
35
31
  if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
@@ -55,67 +51,101 @@ export default function SearchInput({
55
51
  setTimeout(() => inputRef.current?.focus(), 50);
56
52
  };
57
53
 
58
- const baseWrapper = "relative flex flex-col font-sans text-xs select-none";
54
+ const baseWrapperStyles: React.CSSProperties = {
55
+ position: 'relative',
56
+ display: 'flex',
57
+ flexDirection: 'column',
58
+ fontFamily: searchTokens.typography.sans, // ⚡ Applied global Tailwind fonts variables
59
+ fontSize: '12px',
60
+ userSelect: 'none',
61
+ boxSizing: 'border-box'
62
+ };
63
+
64
+ const inputContainerBaseStyles: React.CSSProperties = {
65
+ position: 'relative',
66
+ display: 'flex',
67
+ alignItems: 'center',
68
+ backgroundColor: searchTokens.colors.bgInput,
69
+ border: `1px solid ${isInputFocused || (variant === 'float' && isExpanded)
70
+ ? searchTokens.colors.borderAccent
71
+ : isInputHovered
72
+ ? searchTokens.colors.borderAccentHover
73
+ : searchTokens.colors.border
74
+ }`,
75
+ borderRadius: '6px',
76
+ height: '32px',
77
+ transition: searchTokens.transitions.smooth,
78
+ boxSizing: 'border-box'
79
+ };
80
+
81
+ const rawInputStyles: React.CSSProperties = {
82
+ width: '100%',
83
+ height: '100%',
84
+ backgroundColor: 'transparent',
85
+ color: searchTokens.colors.text,
86
+ border: 'none',
87
+ outline: 'none',
88
+ padding: '0 32px',
89
+ fontSize: '12px',
90
+ fontFamily: searchTokens.typography.sans,
91
+ boxSizing: 'border-box'
92
+ };
59
93
 
60
- // --- 1. SIMPLE VARIANT PANEL ---
94
+ // --- 1. SIMPLE VARIANT ---
61
95
  if (variant === 'simple') {
62
- const hasResults = value.length > 0;
96
+ const hasResults = resultsCount > 0 && value.length > 0;
97
+
98
+ // ⚡ Fallback logic: check if the dev supplied a custom peek content block component
99
+ const renderedPeek = peekContent || (
100
+ <p style={{ margin: 0, fontSize: '10px', fontFamily: searchTokens.typography.mono, color: searchTokens.colors.muted, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
101
+ Showing <span style={{ color: searchTokens.colors.borderAccent, fontWeight: 'bold' }}>{resultsCount}</span> matching database metrics records
102
+ </p>
103
+ );
63
104
 
64
105
  return (
65
- <div className={`${baseWrapper} ${className}`.trim()}>
66
- {/* Core Input Box Frame */}
67
- <div className="relative flex items-center bg-vsc-bg-input border border-vsc-border rounded-sm h-8 group hover:border-vsc-accent transition-colors z-20">
68
- <Search size={14} className="absolute left-2.5 text-vsc-muted group-hover:text-vsc-text" />
106
+ <div style={baseWrapperStyles} className={className}>
107
+ <div
108
+ style={inputContainerBaseStyles}
109
+ onMouseEnter={() => setIsInputHovered(true)}
110
+ onMouseLeave={() => setIsInputHovered(false)}
111
+ >
112
+ <div style={{ position: 'absolute', left: '10px', display: 'flex', alignItems: 'center', color: isInputHovered ? searchTokens.colors.text : searchTokens.colors.muted }}>
113
+ <Search size={14} />
114
+ </div>
69
115
  <input
70
116
  ref={inputRef}
71
117
  type="text"
72
118
  value={value}
73
119
  onChange={(e) => onChange(e.target.value)}
120
+ onFocus={() => setIsInputFocused(true)}
121
+ onBlur={() => setIsInputFocused(false)}
74
122
  placeholder={placeholder}
75
- className="w-full h-full pl-8 pr-10 bg-transparent text-vsc-text border-none outline-none focus:outline-none placeholder-vsc-muted"
123
+ style={rawInputStyles}
76
124
  />
77
125
  {value && (
78
- <div className="absolute right-1 top-1/2 -translate-y-1/2 flex items-center">
79
- <Button
80
- icon={Close}
81
- color="ghost"
82
- iconOnly={true}
83
- onClick={handleClearTrigger}
84
- size="xs"
85
- />
126
+ <div style={{ position: 'absolute', right: '4px', top: '50%', transform: 'translateY(-50%)', display: 'flex', alignItems: 'center' }}>
127
+ <Button icon={Close} color="ghost" iconOnly={true} onClick={handleClearTrigger} size="xs" />
86
128
  </div>
87
129
  )}
88
130
  </div>
89
131
 
90
- {/*
91
- 🎛️ HIGH-FIDELITY FLOATING SLIDE-DOWN INDICATOR PANEL
92
- - Uses 'absolute' so it zero-impacts the parent height layout block.
93
- - Utilizes hardware-accelerated transforms (translateY) for a smooth glide effect.
94
- */}
95
132
  <div
96
- className={`absolute left-0 right-0 top-8 z-50 overflow-hidden transition-all duration-200 cubic-bezier(0.34, 1.56, 0.64, 1) ${!hasResults ? 'pointer-events-none' : ''}`}
97
133
  style={{
98
- height: hasResults ? 'auto' : '0px',
134
+ position: 'absolute',
135
+ left: 0,
136
+ right: 0,
137
+ top: '32px',
138
+ zIndex: 10,
139
+ overflow: 'hidden',
140
+ transition: searchTokens.transitions.smooth,
141
+ height: hasResults ? '24px' : '0px',
99
142
  opacity: hasResults ? 1 : 0,
100
- transform: hasResults ? 'translateY(0px)' : 'translateY(-4px)'
143
+ transform: hasResults ? 'translateY(0px)' : 'translateY(-4px)',
144
+ pointerEvents: 'none'
101
145
  }}
102
146
  >
103
- <div className="bg-vsc-sidebar shadow-sm p-1.5 pt-1 ">
104
- {
105
- resultsCount === 0 ? (
106
- <div className="pl-1 text-start text-[11px] text-vsc-muted italic">
107
- No record match your search query constraints.
108
- </div>
109
- ) : (
110
-
111
- resultIndicatorPanel ? resultIndicatorPanel : (
112
- <p className="text-[10px] font-mono text-vsc-muted pl-1 truncate">
113
- Showing <span className="text-vsc-accent font-bold">{resultsCount}</span> matching database metrics records
114
- </p>
115
- )
116
-
117
- )
118
- }
147
+ <div style={{ backgroundColor: searchTokens.colors.sidebarBg, border: `1px solid ${searchTokens.colors.border}`, borderTop: 'none', padding: '4px 6px', borderRadius: '0 0 6px 6px', boxShadow: '0 4px 6px -1px rgba(0,0,0,0.1)' }}>
148
+ {renderedPeek}
119
149
  </div>
120
150
  </div>
121
151
  </div>
@@ -125,32 +155,42 @@ export default function SearchInput({
125
155
  // --- 2. FLOAT EXPANDABLE VARIANT ---
126
156
  if (variant === 'float') {
127
157
  const showPeekIndicator = showFloatPeek && resultsCount > 0 && value.length > 0;
158
+ const isOpened = isExpanded || value.length > 0;
159
+
160
+ const renderedFloatPeek = peekContent || (
161
+ <p style={{ margin: 0, fontSize: '9px', fontFamily: searchTokens.typography.mono, color: searchTokens.colors.borderAccent, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
162
+ Quick Peek: Found {resultsCount} rows
163
+ </p>
164
+ );
165
+
128
166
  return (
129
- <div ref={containerRef} className={`${baseWrapper} ${className}`.trim()}>
167
+ <div ref={containerRef} style={baseWrapperStyles} className={className}>
130
168
  <div
131
- className={`flex items-center bg-vsc-bg-input border rounded-sm h-8 transition-all duration-300 ease-in-out overflow-hidden ${isExpanded || value ? 'w-64 border-vsc-accent px-2' : 'w-8 border-transparent bg-transparent justify-center'
132
- }`}
169
+ onMouseEnter={() => setIsInputHovered(true)}
170
+ onMouseLeave={() => setIsInputHovered(false)}
171
+ style={{
172
+ ...inputContainerBaseStyles,
173
+ width: isOpened ? '256px' : '32px',
174
+ justifyContent: isOpened ? 'flex-start' : 'center',
175
+ backgroundColor: isOpened ? searchTokens.colors.bgInput : 'transparent',
176
+ borderColor: isOpened ? searchTokens.colors.borderAccent : 'transparent',
177
+ padding: isOpened ? '0 8px' : '0'
178
+ }}
133
179
  >
134
- {isExpanded || value ? (
135
- <div className="relative flex items-center w-full h-full">
136
- <Search size={14} className="text-vsc-text shrink-0" />
180
+ {isOpened ? (
181
+ <div style={{ position: 'relative', display: 'flex', alignItems: 'center', width: '100%', height: '100%' }}>
182
+ <Search size={14} style={{ color: searchTokens.colors.text, flexShrink: 0 }} />
137
183
  <input
138
184
  ref={inputRef}
139
185
  type="text"
140
186
  value={value}
141
187
  onChange={(e) => onChange(e.target.value)}
142
188
  placeholder={placeholder}
143
- className="w-full h-full pl-2 pr-6 bg-transparent text-vsc-text border-none outline-none focus:outline-none placeholder-vsc-muted"
189
+ style={{ ...rawInputStyles, padding: '0 24px 0 8px' }}
144
190
  />
145
191
  {value && (
146
- <div className="absolute right-0 top-1/2 -translate-y-1/2 flex items-center">
147
- <Button
148
- iconOnly={true}
149
- color="ghost"
150
- icon={Close}
151
- onClick={handleClearTrigger}
152
- size="xs"
153
- />
192
+ <div style={{ position: 'absolute', right: 0, top: '50%', transform: 'translateY(-50%)', display: 'flex', alignItems: 'center' }}>
193
+ <Button iconOnly={true} color="ghost" icon={Close} onClick={handleClearTrigger} size="xs" />
154
194
  </div>
155
195
  )}
156
196
  </div>
@@ -161,33 +201,45 @@ export default function SearchInput({
161
201
  icon={Search}
162
202
  onClick={handleFloatActivation}
163
203
  color="ghost"
164
- className="text-vsc-muted hover:text-vsc-text transition-colors"
204
+ style={{ color: searchTokens.colors.muted }}
165
205
  title="Open Expandable Search"
166
206
  />
167
207
  )}
168
208
  </div>
169
209
 
170
- {/* Optional dynamic slider line badge dropdown block */}
171
210
  <div
172
- className="overflow-hidden transition-all duration-200 ease-out w-64 absolute top-8"
173
211
  style={{
174
- height: showPeekIndicator ? '20px' : '0px',
175
- opacity: showPeekIndicator ? 1 : 0
212
+ position: 'absolute',
213
+ top: '32px',
214
+ width: '256px',
215
+ zIndex: 10,
216
+ overflow: 'hidden',
217
+ transition: searchTokens.transitions.smooth,
218
+ height: showPeekIndicator ? '24px' : '0px',
219
+ opacity: showPeekIndicator ? 1 : 0,
220
+ transform: showPeekIndicator ? 'translateY(0px)' : 'translateY(-4px)',
221
+ pointerEvents: 'none'
176
222
  }}
177
223
  >
178
- <p className="text-[9px] font-mono text-vsc-accent pt-1 pl-1 truncate bg-vsc-sidebar border border-t-0 border-vsc-border p-1 rounded-b shadow-sm">
179
- Quick Peek: Found {resultsCount} rows
180
- </p>
224
+ <div style={{ backgroundColor: searchTokens.colors.sidebarBg, border: `1px solid ${searchTokens.colors.border}`, borderTop: 'none', padding: '4px', borderRadius: '0 0 2px 2px', boxShadow: '0 4px 6px -1px rgba(0,0,0,0.1)' }}>
225
+ {renderedFloatPeek}
226
+ </div>
181
227
  </div>
182
228
  </div>
183
229
  );
184
230
  }
185
231
 
186
- // --- 3. MENU STYLE SEARCH DROPDOWN ---
232
+ // --- 3. MENU DROPDOWN VARIANT ---
187
233
  return (
188
- <div ref={containerRef} className={`${baseWrapper} ${className}`.trim()}>
189
- <div className="relative flex items-center bg-vsc-bg-input border border-vsc-border rounded-sm h-8 focus-within:border-vsc-accent">
190
- <Search size={14} className="absolute left-2.5 text-vsc-muted" />
234
+ <div ref={containerRef} style={baseWrapperStyles} className={className}>
235
+ <div
236
+ style={inputContainerBaseStyles}
237
+ onMouseEnter={() => setIsInputHovered(true)}
238
+ onMouseLeave={() => setIsInputHovered(false)}
239
+ >
240
+ <div style={{ position: 'absolute', left: '10px', display: 'flex', alignItems: 'center', color: searchTokens.colors.muted }}>
241
+ <Search size={14} />
242
+ </div>
191
243
  <input
192
244
  ref={inputRef}
193
245
  type="text"
@@ -198,65 +250,23 @@ export default function SearchInput({
198
250
  }}
199
251
  onFocus={() => setIsMenuOpen(true)}
200
252
  placeholder={placeholder}
201
- className="w-full h-full pl-8 pr-10 bg-transparent text-vsc-text border-none outline-none focus:outline-none placeholder-vsc-muted"
253
+ style={rawInputStyles}
202
254
  />
203
255
  {value && (
204
- <div className="absolute right-1 top-1/2 -translate-y-1/2 flex items-center">
205
- <Button
206
- iconOnly={true}
207
- icon={Close}
208
- color="ghost"
209
- onClick={handleClearTrigger}
210
- size="xs"
211
- />
256
+ <div style={{ position: 'absolute', right: '4px', top: '50%', transform: 'translateY(-50%)', display: 'flex', alignItems: 'center' }}>
257
+ <Button iconOnly={true} icon={Close} color="ghost" onClick={handleClearTrigger} size="xs" />
212
258
  </div>
213
259
  )}
214
260
  </div>
215
261
 
216
- {isMenuOpen && value && (
217
- <div className="absolute top-9 left-0 w-full bg-vsc-sidebar border border-vsc-border rounded shadow-xl z-50 p-1 flex flex-col max-h-64 animate-in fade-in slide-in-from-top-1 duration-150">
218
- <div className="overflow-y-auto flex-1">
219
- {menuResults.length === 0 ? (
220
- <div className="p-4 text-center text-[11px] text-vsc-muted italic">
221
- No indexed record entities match your text query constraints.
222
- </div>
223
- ) : (
224
- menuResults.map((item) => (
225
- <div
226
- key={item.id}
227
- onClick={() => {
228
- if (onResultClick) onResultClick(item);
229
- setIsMenuOpen(false);
230
- }}
231
- className="w-full p-2 hover:bg-vsc-hover text-left rounded-sm cursor-pointer flex flex-col space-y-0.5 transition-colors"
232
- >
233
- <div className="font-semibold text-vsc-text flex justify-between items-center">
234
- <span className="truncate">{item.title}</span>
235
- {item.category && (
236
- <span className="text-[9px] font-mono font-bold bg-vsc-accent/10 border border-vsc-accent/20 text-vsc-accent px-1 rounded-sm uppercase tracking-wide">
237
- {item.category}
238
- </span>
239
- )}
240
- </div>
241
- {item.subtitle && <div className="text-[10px] text-vsc-muted truncate">{item.subtitle}</div>}
242
- </div>
243
- ))
244
- )}
245
- </div>
246
-
247
- {menuResults.length > 0 && onViewMoreClick && (
248
- <button
249
- onClick={() => {
250
- onViewMoreClick();
251
- setIsMenuOpen(false);
252
- }}
253
- className="w-full border-t border-vsc-border bg-vsc-bg hover:bg-vsc-hover/60 p-2 text-center text-[10px] font-bold text-vsc-accent tracking-wide uppercase transition-colors rounded-b-sm border-none cursor-pointer"
254
- >
255
- View All Matching Search Metrics Records →
256
- </button>
257
- )}
258
- </div>
259
- )}
262
+ <SearchMenu
263
+ isOpen={isMenuOpen}
264
+ value={value}
265
+ menuResults={menuResults}
266
+ onResultClick={onResultClick}
267
+ onViewMoreClick={onViewMoreClick}
268
+ onCloseMenu={() => setIsMenuOpen(false)}
269
+ />
260
270
  </div>
261
271
  );
262
- }
272
+ }
@@ -0,0 +1,119 @@
1
+ import { useState } from 'react';
2
+ import { SearchResultItem, searchTokens } from './types';
3
+
4
+ interface SearchMenuProps {
5
+ isOpen: boolean;
6
+ value: string;
7
+ menuResults: SearchResultItem[];
8
+ onResultClick?: (item: SearchResultItem) => void;
9
+ onViewMoreClick?: () => void;
10
+ onCloseMenu: () => void;
11
+ }
12
+
13
+ export default function SearchMenu({
14
+ isOpen,
15
+ value,
16
+ menuResults,
17
+ onResultClick,
18
+ onViewMoreClick,
19
+ onCloseMenu
20
+ }: SearchMenuProps) {
21
+ const [hoveredResultId, setHoveredResultId] = useState<string | null>(null);
22
+ const [isViewMoreHovered, setIsViewMoreHovered] = useState<boolean>(false);
23
+
24
+ if (!isOpen || !value) return null;
25
+
26
+ return (
27
+ <div
28
+ style={{
29
+ position: 'absolute',
30
+ top: '36px',
31
+ left: 0,
32
+ width: '100%',
33
+ backgroundColor: searchTokens.colors.sidebarBg,
34
+ border: `1px solid ${searchTokens.colors.border}`,
35
+ borderRadius: '4px',
36
+ boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.2)',
37
+ zIndex: 50,
38
+ padding: '4px',
39
+ display: 'flex',
40
+ flexDirection: 'column',
41
+ maxHeight: '256px'
42
+ }}
43
+ >
44
+ <div style={{ overflowY: 'auto', flex: 1 }}>
45
+ {menuResults.length === 0 ? (
46
+ <div style={{ padding: '16px', textAlign: 'center', fontSize: '11px', color: searchTokens.colors.muted, fontStyle: 'italic' }}>
47
+ No indexed record entities match your text query constraints.
48
+ </div>
49
+ ) : (
50
+ menuResults.map((item) => {
51
+ const isItemHovered = hoveredResultId === item.id;
52
+ return (
53
+ <div
54
+ key={item.id}
55
+ onMouseEnter={() => setHoveredResultId(item.id)}
56
+ onMouseLeave={() => setHoveredResultId(null)}
57
+ onClick={() => {
58
+ if (onResultClick) onResultClick(item);
59
+ onCloseMenu();
60
+ }}
61
+ style={{
62
+ width: '100%',
63
+ padding: '8px',
64
+ backgroundColor: isItemHovered ? searchTokens.colors.hoverBg : 'transparent',
65
+ borderRadius: '2px',
66
+ cursor: 'pointer',
67
+ display: 'flex',
68
+ flexDirection: 'column',
69
+ gap: '2px',
70
+ transition: 'background-color 100ms',
71
+ boxSizing: 'border-box'
72
+ }}
73
+ >
74
+ <div style={{ fontWeight: 600, color: searchTokens.colors.text, display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
75
+ <span style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', flex: 1 }}>{item.title}</span>
76
+ {item.category && (
77
+ <span style={{ fontSize: '9px', fontFamily: 'monospace', fontWeight: 'bold', backgroundColor: searchTokens.colors.accentBgLight, border: `1px solid rgba(0, 122, 204, 0.2)`, color: searchTokens.colors.borderAccent, padding: '2px 4px', borderRadius: '2px', textTransform: 'uppercase', marginLeft: '8px', flexShrink: 0 }}>
78
+ {item.category}
79
+ </span>
80
+ )}
81
+ </div>
82
+ {item.subtitle && <div style={{ fontSize: '10px', color: searchTokens.colors.muted, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{item.subtitle}</div>}
83
+ </div>
84
+ );
85
+ })
86
+ )}
87
+ </div>
88
+
89
+ {menuResults.length > 0 && onViewMoreClick && (
90
+ <button
91
+ onMouseEnter={() => setIsViewMoreHovered(true)}
92
+ onMouseLeave={() => setIsViewMoreHovered(false)}
93
+ onClick={() => {
94
+ onViewMoreClick();
95
+ onCloseMenu();
96
+ }}
97
+ style={{
98
+ width: '100%',
99
+ border: 'none',
100
+ borderTop: `1px solid ${searchTokens.colors.border}`,
101
+ backgroundColor: isViewMoreHovered ? searchTokens.colors.hoverBg : 'transparent',
102
+ padding: '8px',
103
+ textAlign: 'center',
104
+ fontSize: '10px',
105
+ fontWeight: 'bold',
106
+ color: searchTokens.colors.borderAccent,
107
+ letterSpacing: '0.5px',
108
+ textTransform: 'uppercase',
109
+ transition: 'background-color 150ms',
110
+ borderRadius: '0 0 2px 2px',
111
+ cursor: 'pointer'
112
+ }}
113
+ >
114
+ View All Matching Search Metrics Records →
115
+ </button>
116
+ )}
117
+ </div>
118
+ );
119
+ }
@@ -1,3 +1,5 @@
1
+ import React from 'react';
2
+
1
3
  export type SearchVariant = 'simple' | 'float' | 'menu';
2
4
 
3
5
  export interface SearchResultItem {
@@ -13,9 +15,34 @@ export interface SearchInputProps {
13
15
  onChange: (value: string) => void;
14
16
  onClear?: () => void;
15
17
  placeholder?: string;
16
- resultsCount?: number; // Mandatory for 'simple' variant display logs
17
- menuResults?: SearchResultItem[]; // Array datasets parsed directly to the 'menu' view dropdown
18
+ resultsCount?: number;
19
+ menuResults?: SearchResultItem[];
18
20
  onResultClick?: (item: SearchResultItem) => void;
19
21
  onViewMoreClick?: () => void;
20
22
  className?: string;
23
+ showFloatPeek?: boolean;
24
+ // ⚡ Accepts either a plain text string label or a fully rendered sub-component node
25
+ peekContent?: React.ReactNode;
21
26
  }
27
+
28
+ export const searchTokens = {
29
+ colors: {
30
+ bgInput: 'var(--color-vsc-bg-input, #f6f8fa)',
31
+ border: 'var(--color-vsc-border, #e4e4e7)',
32
+ borderAccent: 'var(--color-vsc-accent, #007acc)',
33
+ borderAccentHover: 'var(--color-vsc-accent-hover, #0062a3)',
34
+ text: 'var(--color-vsc-text, #333333)',
35
+ muted: 'var(--color-vsc-muted, #6a737d)',
36
+ sidebarBg: 'var(--color-vsc-sidebar, #f3f3f3)',
37
+ hoverBg: 'var(--color-vsc-hover, #e8e8e8)',
38
+ accentBgLight: 'rgba(0, 122, 204, 0.1)'
39
+ },
40
+ typography: {
41
+ /* ⚡ Pulls Tailwind's native font configuration maps from your global layout shell App context */
42
+ sans: 'var(--font-sans, font-sans, ui-sans-serif, system-ui, sans-serif)',
43
+ mono: 'var(--font-mono, font-mono, ui-monospace, SFMono-Regular, monospace)'
44
+ },
45
+ transitions: {
46
+ smooth: 'all 200ms cubic-bezier(0.34, 1.56, 0.64, 1)'
47
+ }
48
+ };
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- import './index.css';
2
1
  export * from './components/resizable';
3
2
  export * from './components/tabs';
4
3
  export * from './components/button';
package/vite.config.mts CHANGED
@@ -9,13 +9,11 @@ import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
9
9
  import { playwright } from '@vitest/browser-playwright';
10
10
  const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
11
11
  import tailwindcss from '@tailwindcss/vite';
12
- import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
13
12
 
14
13
  // More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
15
14
  export default defineConfig({
16
15
  plugins: [
17
16
  tailwindcss(),
18
- cssInjectedByJsPlugin(),
19
17
  react(),
20
18
  // Auto-generates independent TypeScript type declaration files (.d.ts) matching paths
21
19
  dts({
@@ -33,8 +31,6 @@ export default defineConfig({
33
31
  'components/icons/index': resolve(__dirname, 'src/components/icons/index.ts'),
34
32
 
35
33
  },
36
- name: "voxelUi",
37
- fileName: (format) => `index.${format}.js`,
38
34
  formats: ['es'] // Output strictly as modern ES Modules for dead-code elimination (tree-shaking)
39
35
  },
40
36
  rollupOptions: {
@@ -1,205 +0,0 @@
1
- import { t as e } from "./jsx-runtime-Boo2vksn.js";
2
- import { t } from "./Button-BgQwvn3C.js";
3
- import { E as n, S as r } from "./icons-BpfDVwCQ.js";
4
- import { useEffect as i, useRef as a, useState as o } from "react";
5
- //#region src/components/search/SearchInput.tsx
6
- var s = e();
7
- function c({ variant: e = "simple", value: c, onChange: l, onClear: u, placeholder: d = "Search manifests or tracking codes...", resultsCount: f = 0, menuResults: p = [], onResultClick: m, onViewMoreClick: h, className: g = "", showFloatPeek: _ = !1, resultIndicatorPanel: v }) {
8
- let [y, b] = o(!1), [x, S] = o(!1), C = a(null), w = a(null);
9
- i(() => {
10
- let t = (t) => {
11
- C.current && !C.current.contains(t.target) && (S(!1), e === "float" && c === "" && b(!1));
12
- };
13
- return window.addEventListener("mousedown", t), () => window.removeEventListener("mousedown", t);
14
- }, [e, c]);
15
- let T = (e) => {
16
- e.stopPropagation(), l(""), u && u(), w.current && w.current.focus();
17
- }, E = () => {
18
- b(!0), setTimeout(() => w.current?.focus(), 50);
19
- }, D = "relative flex flex-col font-sans text-xs select-none";
20
- if (e === "simple") {
21
- let e = c.length > 0;
22
- return /* @__PURE__ */ (0, s.jsxs)("div", {
23
- className: `${D} ${g}`.trim(),
24
- children: [/* @__PURE__ */ (0, s.jsxs)("div", {
25
- className: "relative flex items-center bg-vsc-bg-input border border-vsc-border rounded-sm h-8 group hover:border-vsc-accent transition-colors z-20",
26
- children: [
27
- /* @__PURE__ */ (0, s.jsx)(r, {
28
- size: 14,
29
- className: "absolute left-2.5 text-vsc-muted group-hover:text-vsc-text"
30
- }),
31
- /* @__PURE__ */ (0, s.jsx)("input", {
32
- ref: w,
33
- type: "text",
34
- value: c,
35
- onChange: (e) => l(e.target.value),
36
- placeholder: d,
37
- className: "w-full h-full pl-8 pr-10 bg-transparent text-vsc-text border-none outline-none focus:outline-none placeholder-vsc-muted"
38
- }),
39
- c && /* @__PURE__ */ (0, s.jsx)("div", {
40
- className: "absolute right-1 top-1/2 -translate-y-1/2 flex items-center",
41
- children: /* @__PURE__ */ (0, s.jsx)(t, {
42
- icon: n,
43
- color: "ghost",
44
- iconOnly: !0,
45
- onClick: T,
46
- size: "xs"
47
- })
48
- })
49
- ]
50
- }), /* @__PURE__ */ (0, s.jsx)("div", {
51
- className: `absolute left-0 right-0 top-8 z-50 overflow-hidden transition-all duration-200 cubic-bezier(0.34, 1.56, 0.64, 1) ${e ? "" : "pointer-events-none"}`,
52
- style: {
53
- height: e ? "auto" : "0px",
54
- opacity: +!!e,
55
- transform: e ? "translateY(0px)" : "translateY(-4px)"
56
- },
57
- children: /* @__PURE__ */ (0, s.jsx)("div", {
58
- className: "bg-vsc-sidebar shadow-sm p-1.5 pt-1 ",
59
- children: f === 0 ? /* @__PURE__ */ (0, s.jsx)("div", {
60
- className: "pl-1 text-start text-[11px] text-vsc-muted italic",
61
- children: "No record match your search query constraints."
62
- }) : v || /* @__PURE__ */ (0, s.jsxs)("p", {
63
- className: "text-[10px] font-mono text-vsc-muted pl-1 truncate",
64
- children: [
65
- "Showing ",
66
- /* @__PURE__ */ (0, s.jsx)("span", {
67
- className: "text-vsc-accent font-bold",
68
- children: f
69
- }),
70
- " matching database metrics records"
71
- ]
72
- })
73
- })
74
- })]
75
- });
76
- }
77
- if (e === "float") {
78
- let e = _ && f > 0 && c.length > 0;
79
- return /* @__PURE__ */ (0, s.jsxs)("div", {
80
- ref: C,
81
- className: `${D} ${g}`.trim(),
82
- children: [/* @__PURE__ */ (0, s.jsx)("div", {
83
- className: `flex items-center bg-vsc-bg-input border rounded-sm h-8 transition-all duration-300 ease-in-out overflow-hidden ${y || c ? "w-64 border-vsc-accent px-2" : "w-8 border-transparent bg-transparent justify-center"}`,
84
- children: y || c ? /* @__PURE__ */ (0, s.jsxs)("div", {
85
- className: "relative flex items-center w-full h-full",
86
- children: [
87
- /* @__PURE__ */ (0, s.jsx)(r, {
88
- size: 14,
89
- className: "text-vsc-text shrink-0"
90
- }),
91
- /* @__PURE__ */ (0, s.jsx)("input", {
92
- ref: w,
93
- type: "text",
94
- value: c,
95
- onChange: (e) => l(e.target.value),
96
- placeholder: d,
97
- className: "w-full h-full pl-2 pr-6 bg-transparent text-vsc-text border-none outline-none focus:outline-none placeholder-vsc-muted"
98
- }),
99
- c && /* @__PURE__ */ (0, s.jsx)("div", {
100
- className: "absolute right-0 top-1/2 -translate-y-1/2 flex items-center",
101
- children: /* @__PURE__ */ (0, s.jsx)(t, {
102
- iconOnly: !0,
103
- color: "ghost",
104
- icon: n,
105
- onClick: T,
106
- size: "xs"
107
- })
108
- })
109
- ]
110
- }) : /* @__PURE__ */ (0, s.jsx)(t, {
111
- iconOnly: !0,
112
- size: "sm",
113
- icon: r,
114
- onClick: E,
115
- color: "ghost",
116
- className: "text-vsc-muted hover:text-vsc-text transition-colors",
117
- title: "Open Expandable Search"
118
- })
119
- }), /* @__PURE__ */ (0, s.jsx)("div", {
120
- className: "overflow-hidden transition-all duration-200 ease-out w-64 absolute top-8",
121
- style: {
122
- height: e ? "20px" : "0px",
123
- opacity: +!!e
124
- },
125
- children: /* @__PURE__ */ (0, s.jsxs)("p", {
126
- className: "text-[9px] font-mono text-vsc-accent pt-1 pl-1 truncate bg-vsc-sidebar border border-t-0 border-vsc-border p-1 rounded-b shadow-sm",
127
- children: [
128
- "Quick Peek: Found ",
129
- f,
130
- " rows"
131
- ]
132
- })
133
- })]
134
- });
135
- }
136
- return /* @__PURE__ */ (0, s.jsxs)("div", {
137
- ref: C,
138
- className: `${D} ${g}`.trim(),
139
- children: [/* @__PURE__ */ (0, s.jsxs)("div", {
140
- className: "relative flex items-center bg-vsc-bg-input border border-vsc-border rounded-sm h-8 focus-within:border-vsc-accent",
141
- children: [
142
- /* @__PURE__ */ (0, s.jsx)(r, {
143
- size: 14,
144
- className: "absolute left-2.5 text-vsc-muted"
145
- }),
146
- /* @__PURE__ */ (0, s.jsx)("input", {
147
- ref: w,
148
- type: "text",
149
- value: c,
150
- onChange: (e) => {
151
- l(e.target.value), S(!0);
152
- },
153
- onFocus: () => S(!0),
154
- placeholder: d,
155
- className: "w-full h-full pl-8 pr-10 bg-transparent text-vsc-text border-none outline-none focus:outline-none placeholder-vsc-muted"
156
- }),
157
- c && /* @__PURE__ */ (0, s.jsx)("div", {
158
- className: "absolute right-1 top-1/2 -translate-y-1/2 flex items-center",
159
- children: /* @__PURE__ */ (0, s.jsx)(t, {
160
- iconOnly: !0,
161
- icon: n,
162
- color: "ghost",
163
- onClick: T,
164
- size: "xs"
165
- })
166
- })
167
- ]
168
- }), x && c && /* @__PURE__ */ (0, s.jsxs)("div", {
169
- className: "absolute top-9 left-0 w-full bg-vsc-sidebar border border-vsc-border rounded shadow-xl z-50 p-1 flex flex-col max-h-64 animate-in fade-in slide-in-from-top-1 duration-150",
170
- children: [/* @__PURE__ */ (0, s.jsx)("div", {
171
- className: "overflow-y-auto flex-1",
172
- children: p.length === 0 ? /* @__PURE__ */ (0, s.jsx)("div", {
173
- className: "p-4 text-center text-[11px] text-vsc-muted italic",
174
- children: "No indexed record entities match your text query constraints."
175
- }) : p.map((e) => /* @__PURE__ */ (0, s.jsxs)("div", {
176
- onClick: () => {
177
- m && m(e), S(!1);
178
- },
179
- className: "w-full p-2 hover:bg-vsc-hover text-left rounded-sm cursor-pointer flex flex-col space-y-0.5 transition-colors",
180
- children: [/* @__PURE__ */ (0, s.jsxs)("div", {
181
- className: "font-semibold text-vsc-text flex justify-between items-center",
182
- children: [/* @__PURE__ */ (0, s.jsx)("span", {
183
- className: "truncate",
184
- children: e.title
185
- }), e.category && /* @__PURE__ */ (0, s.jsx)("span", {
186
- className: "text-[9px] font-mono font-bold bg-vsc-accent/10 border border-vsc-accent/20 text-vsc-accent px-1 rounded-sm uppercase tracking-wide",
187
- children: e.category
188
- })]
189
- }), e.subtitle && /* @__PURE__ */ (0, s.jsx)("div", {
190
- className: "text-[10px] text-vsc-muted truncate",
191
- children: e.subtitle
192
- })]
193
- }, e.id))
194
- }), p.length > 0 && h && /* @__PURE__ */ (0, s.jsx)("button", {
195
- onClick: () => {
196
- h(), S(!1);
197
- },
198
- className: "w-full border-t border-vsc-border bg-vsc-bg hover:bg-vsc-hover/60 p-2 text-center text-[10px] font-bold text-vsc-accent tracking-wide uppercase transition-colors rounded-b-sm border-none cursor-pointer",
199
- children: "View All Matching Search Metrics Records →"
200
- })]
201
- })]
202
- });
203
- }
204
- //#endregion
205
- export { c as t };