@neoptocom/neopto-ui 0.2.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.
- package/CONSUMER_SETUP.md +127 -0
- package/README.md +186 -0
- package/dist/index.cjs +836 -0
- package/dist/index.d.cts +290 -0
- package/dist/index.d.ts +290 -0
- package/dist/index.js +805 -0
- package/dist/styles.css +109 -0
- package/package.json +89 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React2 = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var reactDom = require('react-dom');
|
|
6
|
+
var tailwindVariants = require('tailwind-variants');
|
|
7
|
+
|
|
8
|
+
function _interopNamespace(e) {
|
|
9
|
+
if (e && e.__esModule) return e;
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
|
|
27
|
+
|
|
28
|
+
// src/components/Input.tsx
|
|
29
|
+
var Input = React2__namespace.forwardRef(
|
|
30
|
+
({ className, disabled, ...props }, ref) => {
|
|
31
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
32
|
+
"input",
|
|
33
|
+
{
|
|
34
|
+
ref,
|
|
35
|
+
disabled,
|
|
36
|
+
className: [
|
|
37
|
+
"w-full h-12 px-4 rounded-full border bg-transparent outline-none transition-colors",
|
|
38
|
+
"text-sm placeholder:text-[var(--muted-fg)]",
|
|
39
|
+
disabled ? "border-[#3F424F] text-[#3F424F] cursor-not-allowed" : [
|
|
40
|
+
"text-[var(--muted-fg)]",
|
|
41
|
+
"border-[var(--muted-fg)]",
|
|
42
|
+
"hover:border-[var(--border)]",
|
|
43
|
+
"focus:border-[var(--color-brand)] focus:text-[var(--fg)]"
|
|
44
|
+
].join(" "),
|
|
45
|
+
className
|
|
46
|
+
].join(" "),
|
|
47
|
+
...props
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
Input.displayName = "Input";
|
|
53
|
+
function useIsomorphicLayoutEffect(effect, deps) {
|
|
54
|
+
const useEffectHook = typeof window !== "undefined" ? React2__namespace.useLayoutEffect : React2__namespace.useEffect;
|
|
55
|
+
useEffectHook(effect, deps);
|
|
56
|
+
}
|
|
57
|
+
function Modal({
|
|
58
|
+
open,
|
|
59
|
+
onClose,
|
|
60
|
+
title,
|
|
61
|
+
closeOnOverlay = true,
|
|
62
|
+
children
|
|
63
|
+
}) {
|
|
64
|
+
const [mounted, setMounted] = React2__namespace.useState(false);
|
|
65
|
+
useIsomorphicLayoutEffect(() => {
|
|
66
|
+
setMounted(true);
|
|
67
|
+
if (!open) return;
|
|
68
|
+
const original = document.body.style.overflow;
|
|
69
|
+
document.body.style.overflow = "hidden";
|
|
70
|
+
return () => {
|
|
71
|
+
document.body.style.overflow = original;
|
|
72
|
+
};
|
|
73
|
+
}, [open]);
|
|
74
|
+
React2__namespace.useEffect(() => {
|
|
75
|
+
if (!open) return;
|
|
76
|
+
const onKey = (e) => {
|
|
77
|
+
if (e.key === "Escape") onClose?.();
|
|
78
|
+
};
|
|
79
|
+
window.addEventListener("keydown", onKey);
|
|
80
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
81
|
+
}, [open, onClose]);
|
|
82
|
+
if (!mounted) return null;
|
|
83
|
+
if (!open) return null;
|
|
84
|
+
const overlay = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
85
|
+
"div",
|
|
86
|
+
{
|
|
87
|
+
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
88
|
+
"aria-labelledby": "modal-title",
|
|
89
|
+
role: "dialog",
|
|
90
|
+
"aria-modal": "true",
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
93
|
+
"div",
|
|
94
|
+
{
|
|
95
|
+
className: "absolute inset-0 bg-black/50",
|
|
96
|
+
onClick: () => closeOnOverlay && onClose?.()
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative z-10 w-full max-w-lg rounded-[var(--radius-lg)] bg-[var(--surface)] text-[var(--fg)] p-6 shadow-xl", children: [
|
|
100
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "modal-title", className: "mb-2 text-lg font-semibold", children: title }) : null,
|
|
101
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children }),
|
|
102
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex justify-end gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
103
|
+
"button",
|
|
104
|
+
{
|
|
105
|
+
onClick: onClose,
|
|
106
|
+
className: "btn btn-outline",
|
|
107
|
+
type: "button",
|
|
108
|
+
children: "Close"
|
|
109
|
+
}
|
|
110
|
+
) })
|
|
111
|
+
] })
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
const container = document.body;
|
|
116
|
+
return reactDom.createPortal(overlay, container);
|
|
117
|
+
}
|
|
118
|
+
var styles = tailwindVariants.tv({
|
|
119
|
+
base: "text-[var(--fg)] -webkit-font-smoothing antialiased -moz-osx-font-smoothing grayscale",
|
|
120
|
+
variants: {
|
|
121
|
+
variant: {
|
|
122
|
+
"display-lg": "text-5xl leading-tight",
|
|
123
|
+
"display-md": "text-4xl leading-tight",
|
|
124
|
+
"display-sm": "text-4xl leading-tight",
|
|
125
|
+
"headline-lg": "text-3xl leading-tight",
|
|
126
|
+
"headline-md": "text-3xl leading-tight",
|
|
127
|
+
"headline-sm": "text-3xl leading-tight",
|
|
128
|
+
"title-lg": "text-xl leading-tight",
|
|
129
|
+
"title-md": "text-lg leading-tight",
|
|
130
|
+
"title-sm": "text-base leading-tight",
|
|
131
|
+
"label-lg": "text-sm leading-tight",
|
|
132
|
+
"label-md": "text-xs leading-tight",
|
|
133
|
+
"label-sm": "text-xs leading-tight",
|
|
134
|
+
"body-lg": "text-base leading-relaxed",
|
|
135
|
+
"body-md": "text-sm leading-relaxed",
|
|
136
|
+
"body-sm": "text-xs leading-relaxed",
|
|
137
|
+
"button": "text-base leading-normal"
|
|
138
|
+
},
|
|
139
|
+
weight: {
|
|
140
|
+
normal: "font-normal",
|
|
141
|
+
medium: "font-medium",
|
|
142
|
+
semibold: "font-semibold",
|
|
143
|
+
bold: "font-bold"
|
|
144
|
+
},
|
|
145
|
+
muted: {
|
|
146
|
+
true: "text-[var(--muted-fg)]"
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
defaultVariants: {
|
|
150
|
+
weight: "normal"
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
function Typo({
|
|
154
|
+
variant,
|
|
155
|
+
bold,
|
|
156
|
+
muted,
|
|
157
|
+
as,
|
|
158
|
+
className,
|
|
159
|
+
children,
|
|
160
|
+
...props
|
|
161
|
+
}) {
|
|
162
|
+
const Component = as ?? "span";
|
|
163
|
+
const getFontFamily = (variant2) => {
|
|
164
|
+
if (variant2.startsWith("body")) {
|
|
165
|
+
return "var(--font-body)";
|
|
166
|
+
}
|
|
167
|
+
return "var(--font-display)";
|
|
168
|
+
};
|
|
169
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
170
|
+
Component,
|
|
171
|
+
{
|
|
172
|
+
className: styles({ variant, weight: bold, muted, className }),
|
|
173
|
+
style: { fontFamily: getFontFamily(variant) },
|
|
174
|
+
...props,
|
|
175
|
+
children
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
var avatarStyles = tailwindVariants.tv({
|
|
180
|
+
base: "relative box-border flex items-center justify-center overflow-hidden rounded-full border border-[var(--border)] bg-[var(--muted)] text-[var(--fg)] select-none",
|
|
181
|
+
variants: {
|
|
182
|
+
size: {
|
|
183
|
+
sm: "w-[28px] h-[28px]",
|
|
184
|
+
md: "w-[60px] h-[60px]"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
defaultVariants: { size: "sm" }
|
|
188
|
+
});
|
|
189
|
+
function getInitials(name) {
|
|
190
|
+
if (!name) return "\u2026";
|
|
191
|
+
const words = name.trim().split(/\s+/);
|
|
192
|
+
if (words.length === 1) return (words[0][0] ?? "").toUpperCase();
|
|
193
|
+
return ((words[0][0] ?? "") + (words[words.length - 1][0] ?? "")).toUpperCase();
|
|
194
|
+
}
|
|
195
|
+
function Avatar({
|
|
196
|
+
name,
|
|
197
|
+
src,
|
|
198
|
+
color,
|
|
199
|
+
size,
|
|
200
|
+
alt,
|
|
201
|
+
className,
|
|
202
|
+
style,
|
|
203
|
+
...props
|
|
204
|
+
}) {
|
|
205
|
+
const [imgError, setImgError] = React2.useState(false);
|
|
206
|
+
const initials = React2.useMemo(() => getInitials(name), [name]);
|
|
207
|
+
const computedStyle = React2.useMemo(() => {
|
|
208
|
+
const s = { ...style };
|
|
209
|
+
if (color) s.backgroundColor = color;
|
|
210
|
+
return s;
|
|
211
|
+
}, [color, style]);
|
|
212
|
+
const textVariant = size === "sm" ? "label-md" : "headline-md";
|
|
213
|
+
const showImage = !!src && !imgError;
|
|
214
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
215
|
+
"div",
|
|
216
|
+
{
|
|
217
|
+
className: avatarStyles({ size, className }),
|
|
218
|
+
"aria-label": alt ?? name,
|
|
219
|
+
role: "img",
|
|
220
|
+
...props,
|
|
221
|
+
style: computedStyle,
|
|
222
|
+
children: showImage ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
223
|
+
"img",
|
|
224
|
+
{
|
|
225
|
+
src,
|
|
226
|
+
alt: alt ?? name,
|
|
227
|
+
className: "absolute inset-0 h-full w-full object-cover rounded-full",
|
|
228
|
+
onError: () => setImgError(true),
|
|
229
|
+
draggable: false
|
|
230
|
+
}
|
|
231
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(Typo, { variant: textVariant, bold: "medium", className: "pointer-events-none", children: initials })
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
function AvatarGroup({
|
|
236
|
+
children,
|
|
237
|
+
className = "",
|
|
238
|
+
max = 5,
|
|
239
|
+
overlapPx = 8,
|
|
240
|
+
withRings = true
|
|
241
|
+
}) {
|
|
242
|
+
const avatars = React2__namespace.Children.toArray(children);
|
|
243
|
+
const displayAvatars = typeof max === "number" ? avatars.slice(0, max) : avatars;
|
|
244
|
+
const extraCount = typeof max === "number" && avatars.length > max ? avatars.length - max : 0;
|
|
245
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ["flex items-center", className].filter(Boolean).join(" "), children: [
|
|
246
|
+
displayAvatars.map((child, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
247
|
+
"div",
|
|
248
|
+
{
|
|
249
|
+
className: [
|
|
250
|
+
"relative",
|
|
251
|
+
i > 0 ? "" : "ml-0"
|
|
252
|
+
].join(" "),
|
|
253
|
+
style: {
|
|
254
|
+
marginLeft: i === 0 ? 0 : -overlapPx,
|
|
255
|
+
zIndex: 100 + i
|
|
256
|
+
},
|
|
257
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: withRings ? "ring-2 ring-[var(--surface)] rounded-full" : "", children: child })
|
|
258
|
+
},
|
|
259
|
+
i
|
|
260
|
+
)),
|
|
261
|
+
extraCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
262
|
+
"span",
|
|
263
|
+
{
|
|
264
|
+
className: "ml-2 inline-flex h-7 w-7 items-center justify-center rounded-full border border-[var(--border)] bg-[var(--muted)] text-[var(--fg)] text-xs",
|
|
265
|
+
"aria-label": `${extraCount} more`,
|
|
266
|
+
title: `${extraCount} more`,
|
|
267
|
+
children: [
|
|
268
|
+
"+",
|
|
269
|
+
extraCount
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
] });
|
|
274
|
+
}
|
|
275
|
+
var roundMap = {
|
|
276
|
+
none: "rounded-none",
|
|
277
|
+
sm: "rounded-[var(--radius-sm)]",
|
|
278
|
+
md: "rounded-[var(--radius-md)]",
|
|
279
|
+
lg: "rounded-[var(--radius-lg)]",
|
|
280
|
+
xl: "rounded-[var(--radius-xl)]",
|
|
281
|
+
full: "rounded-full"
|
|
282
|
+
};
|
|
283
|
+
function Skeleton({ className = "", rounded = "md", ...props }) {
|
|
284
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
285
|
+
"div",
|
|
286
|
+
{
|
|
287
|
+
className: [
|
|
288
|
+
"animate-pulse bg-[var(--muted)]",
|
|
289
|
+
roundMap[rounded],
|
|
290
|
+
className
|
|
291
|
+
].join(" "),
|
|
292
|
+
...props
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
var sizeMap = {
|
|
297
|
+
sm: 16,
|
|
298
|
+
md: 24,
|
|
299
|
+
lg: 36
|
|
300
|
+
};
|
|
301
|
+
function Icon({
|
|
302
|
+
name,
|
|
303
|
+
className = "",
|
|
304
|
+
title,
|
|
305
|
+
size = "md",
|
|
306
|
+
fill = 0
|
|
307
|
+
}) {
|
|
308
|
+
const fontSize = sizeMap[size] ?? sizeMap.md;
|
|
309
|
+
const hasColorClass = /\b(?:text-|fill-|stroke-)\S+/.test(className);
|
|
310
|
+
const style = {
|
|
311
|
+
fontVariationSettings: `'FILL' ${fill}, 'wght' 300, 'GRAD' 0, 'opsz' ${fontSize}`,
|
|
312
|
+
fontSize,
|
|
313
|
+
lineHeight: 1,
|
|
314
|
+
display: "inline-block",
|
|
315
|
+
verticalAlign: "middle",
|
|
316
|
+
...hasColorClass ? {} : { color: "inherit" }
|
|
317
|
+
};
|
|
318
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
319
|
+
"span",
|
|
320
|
+
{
|
|
321
|
+
className: `material-symbols-rounded rounded ${className}`,
|
|
322
|
+
style,
|
|
323
|
+
"aria-hidden": title ? void 0 : true,
|
|
324
|
+
title,
|
|
325
|
+
children: name
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
function Autocomplete({
|
|
330
|
+
className = "",
|
|
331
|
+
title,
|
|
332
|
+
options,
|
|
333
|
+
selectedOption,
|
|
334
|
+
onSelect,
|
|
335
|
+
placeholder = "",
|
|
336
|
+
disabled = false,
|
|
337
|
+
maxHeight = 152,
|
|
338
|
+
id,
|
|
339
|
+
...props
|
|
340
|
+
}) {
|
|
341
|
+
const inputId = id ?? React2.useId();
|
|
342
|
+
const listboxId = `${inputId}-listbox`;
|
|
343
|
+
const [searchQuery, setSearchQuery] = React2.useState("");
|
|
344
|
+
const [open, setOpen] = React2.useState(false);
|
|
345
|
+
const [activeIndex, setActiveIndex] = React2.useState(-1);
|
|
346
|
+
const rootRef = React2.useRef(null);
|
|
347
|
+
const listRef = React2.useRef(null);
|
|
348
|
+
const normalizedOptions = React2.useMemo(() => {
|
|
349
|
+
if (Array.isArray(options) && typeof options[0] === "string") {
|
|
350
|
+
return options.map((str) => ({ label: str, value: str }));
|
|
351
|
+
}
|
|
352
|
+
return options;
|
|
353
|
+
}, [options]);
|
|
354
|
+
const filtered = React2.useMemo(() => {
|
|
355
|
+
const q = searchQuery.trim().toLowerCase();
|
|
356
|
+
if (!q) return normalizedOptions;
|
|
357
|
+
return normalizedOptions.filter((o) => o.label.toLowerCase().includes(q));
|
|
358
|
+
}, [normalizedOptions, searchQuery]);
|
|
359
|
+
const anyOptionHasImage = React2.useMemo(
|
|
360
|
+
() => normalizedOptions.some((o) => !!o.image),
|
|
361
|
+
[normalizedOptions]
|
|
362
|
+
);
|
|
363
|
+
const displayValue = selectedOption != null ? typeof selectedOption === "string" ? selectedOption : selectedOption.label : searchQuery;
|
|
364
|
+
function openList() {
|
|
365
|
+
if (disabled) return;
|
|
366
|
+
setOpen(true);
|
|
367
|
+
}
|
|
368
|
+
function closeList() {
|
|
369
|
+
setOpen(false);
|
|
370
|
+
setActiveIndex(-1);
|
|
371
|
+
}
|
|
372
|
+
function handleSelect(option) {
|
|
373
|
+
onSelect(option);
|
|
374
|
+
setSearchQuery("");
|
|
375
|
+
closeList();
|
|
376
|
+
}
|
|
377
|
+
function handleClear() {
|
|
378
|
+
onSelect(null);
|
|
379
|
+
setSearchQuery("");
|
|
380
|
+
closeList();
|
|
381
|
+
}
|
|
382
|
+
function onKeyDown(e) {
|
|
383
|
+
if (!open && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
|
|
384
|
+
setOpen(true);
|
|
385
|
+
setActiveIndex(0);
|
|
386
|
+
e.preventDefault();
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (!open) return;
|
|
390
|
+
if (e.key === "ArrowDown") {
|
|
391
|
+
e.preventDefault();
|
|
392
|
+
setActiveIndex((i) => Math.min(i + 1, filtered.length - 1));
|
|
393
|
+
scrollActiveIntoView();
|
|
394
|
+
} else if (e.key === "ArrowUp") {
|
|
395
|
+
e.preventDefault();
|
|
396
|
+
setActiveIndex((i) => Math.max(i - 1, 0));
|
|
397
|
+
scrollActiveIntoView();
|
|
398
|
+
} else if (e.key === "Enter") {
|
|
399
|
+
e.preventDefault();
|
|
400
|
+
const item = filtered[activeIndex];
|
|
401
|
+
if (item) handleSelect(item);
|
|
402
|
+
} else if (e.key === "Escape") {
|
|
403
|
+
e.preventDefault();
|
|
404
|
+
closeList();
|
|
405
|
+
} else if (e.key === "Home") {
|
|
406
|
+
e.preventDefault();
|
|
407
|
+
setActiveIndex(0);
|
|
408
|
+
scrollActiveIntoView();
|
|
409
|
+
} else if (e.key === "End") {
|
|
410
|
+
e.preventDefault();
|
|
411
|
+
setActiveIndex(filtered.length - 1);
|
|
412
|
+
scrollActiveIntoView();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
function scrollActiveIntoView() {
|
|
416
|
+
const list = listRef.current;
|
|
417
|
+
const idx = activeIndex;
|
|
418
|
+
if (!list || idx < 0) return;
|
|
419
|
+
const el = list.children[idx];
|
|
420
|
+
el?.scrollIntoView({ block: "nearest" });
|
|
421
|
+
}
|
|
422
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
423
|
+
"div",
|
|
424
|
+
{
|
|
425
|
+
ref: rootRef,
|
|
426
|
+
className: ["relative w-full", className].join(" "),
|
|
427
|
+
...props,
|
|
428
|
+
children: [
|
|
429
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
430
|
+
"fieldset",
|
|
431
|
+
{
|
|
432
|
+
className: [
|
|
433
|
+
"w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-16",
|
|
434
|
+
"border-[var(--border)] focus-within:border-[var(--color-brand)]",
|
|
435
|
+
disabled ? "opacity-60 cursor-not-allowed" : ""
|
|
436
|
+
].join(" "),
|
|
437
|
+
children: [
|
|
438
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { className: "ml-4 px-1 text-sm leading-none relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
439
|
+
Typo,
|
|
440
|
+
{
|
|
441
|
+
variant: "label-lg",
|
|
442
|
+
className: [
|
|
443
|
+
"font-normal select-none",
|
|
444
|
+
open ? "text-[var(--color-brand)]" : "text-[var(--muted-fg)]",
|
|
445
|
+
disabled ? "text-[var(--muted-fg)]" : ""
|
|
446
|
+
].join(" "),
|
|
447
|
+
children: title
|
|
448
|
+
}
|
|
449
|
+
) }),
|
|
450
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex pl-5 pr-3 pb-1 h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full", children: [
|
|
451
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
452
|
+
"input",
|
|
453
|
+
{
|
|
454
|
+
id: inputId,
|
|
455
|
+
role: "combobox",
|
|
456
|
+
"aria-expanded": open,
|
|
457
|
+
"aria-controls": listboxId,
|
|
458
|
+
"aria-autocomplete": "list",
|
|
459
|
+
"aria-disabled": disabled || void 0,
|
|
460
|
+
type: "text",
|
|
461
|
+
value: displayValue,
|
|
462
|
+
onChange: (e) => {
|
|
463
|
+
setSearchQuery(e.target.value);
|
|
464
|
+
if (!open) setOpen(true);
|
|
465
|
+
setActiveIndex(0);
|
|
466
|
+
},
|
|
467
|
+
onFocus: openList,
|
|
468
|
+
onKeyDown,
|
|
469
|
+
onBlur: () => setTimeout(closeList, 150),
|
|
470
|
+
disabled,
|
|
471
|
+
className: [
|
|
472
|
+
"w-full rounded-full border-0 outline-none bg-transparent",
|
|
473
|
+
"text-sm text-[var(--fg)] placeholder:text-[var(--muted-fg)]",
|
|
474
|
+
"pr-2"
|
|
475
|
+
].join(" "),
|
|
476
|
+
placeholder,
|
|
477
|
+
onClick: () => !disabled && setOpen(true)
|
|
478
|
+
}
|
|
479
|
+
),
|
|
480
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
481
|
+
"button",
|
|
482
|
+
{
|
|
483
|
+
type: "button",
|
|
484
|
+
onClick: selectedOption && !open ? handleClear : () => setOpen((s) => !s),
|
|
485
|
+
disabled,
|
|
486
|
+
"aria-label": selectedOption && !open ? "Clear" : open ? "Collapse" : "Expand",
|
|
487
|
+
className: [
|
|
488
|
+
"flex items-center justify-center rounded-full bg-transparent w-10 h-10",
|
|
489
|
+
disabled ? "cursor-not-allowed" : "hover:bg-[var(--muted)]"
|
|
490
|
+
].join(" "),
|
|
491
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
492
|
+
Icon,
|
|
493
|
+
{
|
|
494
|
+
name: selectedOption && !open ? "close" : "expand_more",
|
|
495
|
+
size: "md",
|
|
496
|
+
className: [
|
|
497
|
+
"transition-transform duration-300 text-[var(--muted-fg)]",
|
|
498
|
+
open ? "rotate-180 text-[var(--color-brand)]" : ""
|
|
499
|
+
].join(" ")
|
|
500
|
+
}
|
|
501
|
+
)
|
|
502
|
+
}
|
|
503
|
+
)
|
|
504
|
+
] }) })
|
|
505
|
+
]
|
|
506
|
+
}
|
|
507
|
+
),
|
|
508
|
+
open && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
509
|
+
"div",
|
|
510
|
+
{
|
|
511
|
+
className: [
|
|
512
|
+
"absolute z-20 mt-2 w-full overflow-y-auto rounded-3xl border border-[var(--border)]",
|
|
513
|
+
"bg-[var(--surface)] text-[var(--fg)] shadow-md"
|
|
514
|
+
].join(" "),
|
|
515
|
+
style: { maxHeight },
|
|
516
|
+
children: filtered.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { id: listboxId, role: "listbox", ref: listRef, children: filtered.map((option, index) => {
|
|
517
|
+
const active = index === activeIndex;
|
|
518
|
+
const selected = selectedOption != null && (typeof selectedOption === "string" ? selectedOption === option.label : selectedOption.label === option.label);
|
|
519
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
520
|
+
"li",
|
|
521
|
+
{
|
|
522
|
+
role: "option",
|
|
523
|
+
"aria-selected": selected,
|
|
524
|
+
className: [
|
|
525
|
+
"flex items-center justify-between px-4 py-2 text-sm cursor-pointer transition-colors",
|
|
526
|
+
active ? "bg-[var(--muted)]" : "",
|
|
527
|
+
index !== filtered.length - 1 ? "border-b border-[var(--border)]" : ""
|
|
528
|
+
].join(" "),
|
|
529
|
+
onMouseEnter: () => setActiveIndex(index),
|
|
530
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
531
|
+
onClick: () => handleSelect(option),
|
|
532
|
+
children: [
|
|
533
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
534
|
+
anyOptionHasImage && /* @__PURE__ */ jsxRuntime.jsx(Avatar, { name: option.label, src: option.image || void 0 }),
|
|
535
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typo, { variant: "label-lg", className: "font-normal text-[var(--fg)]", children: option.label })
|
|
536
|
+
] }),
|
|
537
|
+
Array.isArray(option.group) && option.group.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(AvatarGroup, { children: option.group.map((member, i) => /* @__PURE__ */ jsxRuntime.jsx(Avatar, { name: member.name, src: member.image || void 0 }, i)) })
|
|
538
|
+
]
|
|
539
|
+
},
|
|
540
|
+
`${option.label}-${index}`
|
|
541
|
+
);
|
|
542
|
+
}) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(Typo, { variant: "body-sm", className: "text-[var(--muted-fg)]", children: "No results found" }) })
|
|
543
|
+
}
|
|
544
|
+
)
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
function Search({
|
|
550
|
+
className = "",
|
|
551
|
+
options,
|
|
552
|
+
onSearch,
|
|
553
|
+
selectedOption,
|
|
554
|
+
onSelect,
|
|
555
|
+
searchDelay = 300,
|
|
556
|
+
disabled = false,
|
|
557
|
+
maxHeight = 152,
|
|
558
|
+
id,
|
|
559
|
+
...props
|
|
560
|
+
}) {
|
|
561
|
+
const inputId = id ?? React2.useId();
|
|
562
|
+
const listboxId = `${inputId}-listbox`;
|
|
563
|
+
const [searchQuery, setSearchQuery] = React2.useState("");
|
|
564
|
+
const [open, setOpen] = React2.useState(false);
|
|
565
|
+
const [activeIndex, setActiveIndex] = React2.useState(-1);
|
|
566
|
+
const rootRef = React2.useRef(null);
|
|
567
|
+
const listRef = React2.useRef(null);
|
|
568
|
+
const searchTimeoutRef = React2.useRef(null);
|
|
569
|
+
const normalizedOptions = React2.useMemo(() => {
|
|
570
|
+
if (Array.isArray(options) && typeof options[0] === "string") {
|
|
571
|
+
return options.map((str) => ({ label: str, value: str }));
|
|
572
|
+
}
|
|
573
|
+
return options;
|
|
574
|
+
}, [options]);
|
|
575
|
+
const anyOptionHasImage = React2.useMemo(
|
|
576
|
+
() => normalizedOptions.some((o) => !!o.image),
|
|
577
|
+
[normalizedOptions]
|
|
578
|
+
);
|
|
579
|
+
const displayValue = selectedOption != null ? typeof selectedOption === "string" ? selectedOption : selectedOption.label : searchQuery;
|
|
580
|
+
const debouncedSearch = React2.useCallback(
|
|
581
|
+
(query) => {
|
|
582
|
+
if (searchTimeoutRef.current) {
|
|
583
|
+
clearTimeout(searchTimeoutRef.current);
|
|
584
|
+
}
|
|
585
|
+
searchTimeoutRef.current = window.setTimeout(() => {
|
|
586
|
+
onSearch(query);
|
|
587
|
+
}, searchDelay);
|
|
588
|
+
},
|
|
589
|
+
[onSearch, searchDelay]
|
|
590
|
+
);
|
|
591
|
+
function openList() {
|
|
592
|
+
if (disabled) return;
|
|
593
|
+
setOpen(true);
|
|
594
|
+
}
|
|
595
|
+
function closeList() {
|
|
596
|
+
setOpen(false);
|
|
597
|
+
setActiveIndex(-1);
|
|
598
|
+
}
|
|
599
|
+
function handleSelect(option) {
|
|
600
|
+
onSelect(option);
|
|
601
|
+
setSearchQuery("");
|
|
602
|
+
closeList();
|
|
603
|
+
}
|
|
604
|
+
function handleClear() {
|
|
605
|
+
onSelect(null);
|
|
606
|
+
setSearchQuery("");
|
|
607
|
+
closeList();
|
|
608
|
+
}
|
|
609
|
+
function onKeyDown(e) {
|
|
610
|
+
if (!open && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
|
|
611
|
+
setOpen(true);
|
|
612
|
+
setActiveIndex(0);
|
|
613
|
+
e.preventDefault();
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
if (!open) return;
|
|
617
|
+
if (e.key === "ArrowDown") {
|
|
618
|
+
e.preventDefault();
|
|
619
|
+
setActiveIndex((i) => Math.min(i + 1, normalizedOptions.length - 1));
|
|
620
|
+
scrollActiveIntoView();
|
|
621
|
+
} else if (e.key === "ArrowUp") {
|
|
622
|
+
e.preventDefault();
|
|
623
|
+
setActiveIndex((i) => Math.max(i - 1, 0));
|
|
624
|
+
scrollActiveIntoView();
|
|
625
|
+
} else if (e.key === "Enter") {
|
|
626
|
+
e.preventDefault();
|
|
627
|
+
const item = normalizedOptions[activeIndex];
|
|
628
|
+
if (item) handleSelect(item);
|
|
629
|
+
} else if (e.key === "Escape") {
|
|
630
|
+
e.preventDefault();
|
|
631
|
+
closeList();
|
|
632
|
+
} else if (e.key === "Home") {
|
|
633
|
+
e.preventDefault();
|
|
634
|
+
setActiveIndex(0);
|
|
635
|
+
scrollActiveIntoView();
|
|
636
|
+
} else if (e.key === "End") {
|
|
637
|
+
e.preventDefault();
|
|
638
|
+
setActiveIndex(normalizedOptions.length - 1);
|
|
639
|
+
scrollActiveIntoView();
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
function scrollActiveIntoView() {
|
|
643
|
+
const list = listRef.current;
|
|
644
|
+
const idx = activeIndex;
|
|
645
|
+
if (!list || idx < 0) return;
|
|
646
|
+
const el = list.children[idx];
|
|
647
|
+
el?.scrollIntoView({ block: "nearest" });
|
|
648
|
+
}
|
|
649
|
+
React2__namespace.useEffect(() => {
|
|
650
|
+
return () => {
|
|
651
|
+
if (searchTimeoutRef.current) {
|
|
652
|
+
clearTimeout(searchTimeoutRef.current);
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
}, []);
|
|
656
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
657
|
+
"div",
|
|
658
|
+
{
|
|
659
|
+
ref: rootRef,
|
|
660
|
+
className: ["relative w-full", className].join(" "),
|
|
661
|
+
...props,
|
|
662
|
+
children: [
|
|
663
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
664
|
+
"div",
|
|
665
|
+
{
|
|
666
|
+
className: [
|
|
667
|
+
"w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-12",
|
|
668
|
+
"border-[var(--border)] focus-within:border-[var(--color-brand)]",
|
|
669
|
+
disabled ? "opacity-60 cursor-not-allowed" : ""
|
|
670
|
+
].join(" "),
|
|
671
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex pl-5 pr-3 h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center", children: [
|
|
672
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
673
|
+
"input",
|
|
674
|
+
{
|
|
675
|
+
id: inputId,
|
|
676
|
+
role: "combobox",
|
|
677
|
+
"aria-expanded": open,
|
|
678
|
+
"aria-controls": listboxId,
|
|
679
|
+
"aria-autocomplete": "list",
|
|
680
|
+
"aria-disabled": disabled || void 0,
|
|
681
|
+
type: "text",
|
|
682
|
+
value: displayValue,
|
|
683
|
+
onChange: (e) => {
|
|
684
|
+
const query = e.target.value;
|
|
685
|
+
setSearchQuery(query);
|
|
686
|
+
debouncedSearch(query);
|
|
687
|
+
if (!open) setOpen(true);
|
|
688
|
+
setActiveIndex(0);
|
|
689
|
+
},
|
|
690
|
+
onFocus: openList,
|
|
691
|
+
onKeyDown,
|
|
692
|
+
onBlur: () => setTimeout(closeList, 150),
|
|
693
|
+
disabled,
|
|
694
|
+
className: [
|
|
695
|
+
"w-full rounded-full border-0 outline-none bg-transparent",
|
|
696
|
+
"text-sm text-[var(--fg)] placeholder:text-[var(--muted-fg)]",
|
|
697
|
+
"pr-2"
|
|
698
|
+
].join(" "),
|
|
699
|
+
placeholder: "Pesquisar",
|
|
700
|
+
onClick: () => !disabled && setOpen(true)
|
|
701
|
+
}
|
|
702
|
+
),
|
|
703
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
704
|
+
"button",
|
|
705
|
+
{
|
|
706
|
+
type: "button",
|
|
707
|
+
onClick: selectedOption && !open ? handleClear : () => setOpen((s) => !s),
|
|
708
|
+
disabled,
|
|
709
|
+
"aria-label": selectedOption && !open ? "Clear" : open ? "Collapse" : "Expand",
|
|
710
|
+
className: [
|
|
711
|
+
"flex items-center justify-center rounded-full bg-transparent w-10 h-10",
|
|
712
|
+
disabled ? "cursor-not-allowed" : "hover:bg-[var(--muted)]"
|
|
713
|
+
].join(" "),
|
|
714
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
715
|
+
Icon,
|
|
716
|
+
{
|
|
717
|
+
name: "search",
|
|
718
|
+
size: "md",
|
|
719
|
+
className: "text-[var(--muted-fg)]"
|
|
720
|
+
}
|
|
721
|
+
)
|
|
722
|
+
}
|
|
723
|
+
)
|
|
724
|
+
] }) })
|
|
725
|
+
}
|
|
726
|
+
),
|
|
727
|
+
open && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
728
|
+
"div",
|
|
729
|
+
{
|
|
730
|
+
className: [
|
|
731
|
+
"absolute z-20 mt-2 w-full overflow-y-auto rounded-lg border border-[var(--border)]",
|
|
732
|
+
"bg-[var(--surface)] text-[var(--fg)] shadow-md backdrop-blur-sm px-3 py-1.5"
|
|
733
|
+
].join(" "),
|
|
734
|
+
style: { maxHeight },
|
|
735
|
+
children: normalizedOptions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { id: listboxId, role: "listbox", ref: listRef, children: normalizedOptions.map((option, index) => {
|
|
736
|
+
const active = index === activeIndex;
|
|
737
|
+
const selected = selectedOption != null && (typeof selectedOption === "string" ? selectedOption === option.label : selectedOption.label === option.label);
|
|
738
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
739
|
+
"li",
|
|
740
|
+
{
|
|
741
|
+
role: "option",
|
|
742
|
+
"aria-selected": selected,
|
|
743
|
+
className: [
|
|
744
|
+
"flex items-center justify-between px-4 py-2 text-sm cursor-pointer transition-colors",
|
|
745
|
+
active ? "bg-[var(--muted)]" : "",
|
|
746
|
+
index !== normalizedOptions.length - 1 ? "border-b border-[var(--border)]" : ""
|
|
747
|
+
].join(" "),
|
|
748
|
+
onMouseEnter: () => setActiveIndex(index),
|
|
749
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
750
|
+
onClick: () => handleSelect(option),
|
|
751
|
+
children: [
|
|
752
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
753
|
+
anyOptionHasImage && /* @__PURE__ */ jsxRuntime.jsx(Avatar, { name: option.label, src: option.image || void 0 }),
|
|
754
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typo, { variant: "label-lg", className: "font-normal text-[var(--fg)]", children: option.label })
|
|
755
|
+
] }),
|
|
756
|
+
Array.isArray(option.group) && option.group.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(AvatarGroup, { children: option.group.map((member, i) => /* @__PURE__ */ jsxRuntime.jsx(Avatar, { name: member.name, src: member.image || void 0 }, i)) })
|
|
757
|
+
]
|
|
758
|
+
},
|
|
759
|
+
`${option.label}-${index}`
|
|
760
|
+
);
|
|
761
|
+
}) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(Typo, { variant: "body-sm", className: "text-[var(--muted-fg)]", children: "No results found" }) })
|
|
762
|
+
}
|
|
763
|
+
)
|
|
764
|
+
]
|
|
765
|
+
}
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
var buttonStyles = tailwindVariants.tv({
|
|
769
|
+
base: "cursor-pointer inline-flex items-center justify-center gap-2 rounded-[var(--radius-2xl)] px-[18px] h-12 text-sm font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-cyan-500/40 disabled:cursor-not-allowed disabled:opacity-50",
|
|
770
|
+
variants: {
|
|
771
|
+
variant: {
|
|
772
|
+
primary: "bg-cyan-500 text-white hover:bg-cyan-400 active:bg-cyan-600 disabled:bg-neutral-400",
|
|
773
|
+
secondary: "border border-cyan-500 text-cyan-500 bg-transparent hover:bg-cyan-50 active:bg-cyan-100 disabled:border-neutral-400 disabled:text-neutral-400",
|
|
774
|
+
ghost: "bg-transparent text-cyan-500 hover:bg-cyan-50 active:bg-cyan-100 disabled:text-neutral-400"
|
|
775
|
+
},
|
|
776
|
+
size: {
|
|
777
|
+
sm: "h-9 px-3 text-sm",
|
|
778
|
+
md: "h-12 px-[18px] text-sm",
|
|
779
|
+
lg: "h-14 px-6 text-base"
|
|
780
|
+
},
|
|
781
|
+
fullWidth: {
|
|
782
|
+
true: "w-full"
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
defaultVariants: {
|
|
786
|
+
variant: "primary",
|
|
787
|
+
size: "md"
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
var Button = React2__namespace.forwardRef(
|
|
791
|
+
({ variant, size, fullWidth, className, children, icon, ...props }, ref) => {
|
|
792
|
+
const content = icon ? /* @__PURE__ */ jsxRuntime.jsx(Typo, { variant: "body-lg", bold: "semibold", className: "text-base font-semibold", children: icon }) : children;
|
|
793
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
794
|
+
"button",
|
|
795
|
+
{
|
|
796
|
+
ref,
|
|
797
|
+
className: buttonStyles({ variant, size, fullWidth, className }),
|
|
798
|
+
...props,
|
|
799
|
+
children: content
|
|
800
|
+
}
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
);
|
|
804
|
+
Button.displayName = "Button";
|
|
805
|
+
function Chip({
|
|
806
|
+
variant = "success",
|
|
807
|
+
icon,
|
|
808
|
+
className = "",
|
|
809
|
+
label,
|
|
810
|
+
...props
|
|
811
|
+
}) {
|
|
812
|
+
const base = "inline-flex w-fit items-center justify-center gap-1 whitespace-nowrap overflow-hidden rounded-full h-6 px-2 text-xs font-semibold border";
|
|
813
|
+
const variantCls = {
|
|
814
|
+
warning: "bg-[var(--warning)] text-white",
|
|
815
|
+
success: "bg-[var(--success)] text-white",
|
|
816
|
+
error: "bg-[var(--destructive)] text-white",
|
|
817
|
+
light: "bg-[var(--muted)] text-[var(--fg)]",
|
|
818
|
+
dark: "bg-[var(--surface)] text-[var(--fg)]"
|
|
819
|
+
};
|
|
820
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: [base, variantCls[variant], className].join(" "), ...props, children: [
|
|
821
|
+
icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, size: "sm", className: "mr-0.5" }) : null,
|
|
822
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: label })
|
|
823
|
+
] });
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
exports.Autocomplete = Autocomplete;
|
|
827
|
+
exports.Avatar = Avatar;
|
|
828
|
+
exports.AvatarGroup = AvatarGroup;
|
|
829
|
+
exports.Button = Button;
|
|
830
|
+
exports.Chip = Chip;
|
|
831
|
+
exports.Icon = Icon;
|
|
832
|
+
exports.Input = Input;
|
|
833
|
+
exports.Modal = Modal;
|
|
834
|
+
exports.Search = Search;
|
|
835
|
+
exports.Skeleton = Skeleton;
|
|
836
|
+
exports.Typo = Typo;
|