@neoptocom/neopto-ui 1.2.1 → 1.3.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.
- package/dist/index.cjs +78 -49
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +44 -16
- package/package.json +1 -1
- package/src/components/Input.tsx +1 -1
- package/src/components/Textarea.tsx +38 -0
- package/src/index.ts +2 -0
- package/src/stories/Textarea.stories.tsx +90 -0
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
-
var
|
|
4
|
+
var React3 = require('react');
|
|
5
5
|
var reactDom = require('react-dom');
|
|
6
6
|
|
|
7
7
|
function _interopNamespace(e) {
|
|
@@ -22,7 +22,7 @@ function _interopNamespace(e) {
|
|
|
22
22
|
return Object.freeze(n);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
var
|
|
25
|
+
var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
|
|
26
26
|
|
|
27
27
|
var __defProp = Object.defineProperty;
|
|
28
28
|
var __export = (target, all) => {
|
|
@@ -107,9 +107,9 @@ function BackgroundBlur({
|
|
|
107
107
|
zIndex = 40,
|
|
108
108
|
className = ""
|
|
109
109
|
}) {
|
|
110
|
-
const [shouldRender, setShouldRender] =
|
|
111
|
-
const [isVisible, setIsVisible] =
|
|
112
|
-
|
|
110
|
+
const [shouldRender, setShouldRender] = React3.useState(false);
|
|
111
|
+
const [isVisible, setIsVisible] = React3.useState(false);
|
|
112
|
+
React3.useEffect(() => {
|
|
113
113
|
if (open) {
|
|
114
114
|
setShouldRender(true);
|
|
115
115
|
requestAnimationFrame(() => {
|
|
@@ -264,7 +264,7 @@ function Card({
|
|
|
264
264
|
}
|
|
265
265
|
);
|
|
266
266
|
}
|
|
267
|
-
var Input =
|
|
267
|
+
var Input = React3__namespace.forwardRef(
|
|
268
268
|
({ className, disabled, variant = "default", ...props }, ref) => {
|
|
269
269
|
const isInline = variant === "inline";
|
|
270
270
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -275,7 +275,7 @@ var Input = React2__namespace.forwardRef(
|
|
|
275
275
|
className: [
|
|
276
276
|
"w-full bg-transparent outline-none transition-colors",
|
|
277
277
|
isInline ? "" : "h-12 px-4 rounded-full",
|
|
278
|
-
"text-sm placeholder:text-[var(--muted-fg)]",
|
|
278
|
+
"font-['Poppins'] text-sm placeholder:text-[var(--muted-fg)]",
|
|
279
279
|
!isInline && "border",
|
|
280
280
|
disabled ? "text-[#3F424F] cursor-not-allowed" + (isInline ? "" : " border-[#3F424F]") : [
|
|
281
281
|
"text-[var(--muted-fg)]",
|
|
@@ -292,8 +292,36 @@ var Input = React2__namespace.forwardRef(
|
|
|
292
292
|
}
|
|
293
293
|
);
|
|
294
294
|
Input.displayName = "Input";
|
|
295
|
+
var Textarea = React3__namespace.forwardRef(
|
|
296
|
+
({ className, disabled, variant = "default", ...props }, ref) => {
|
|
297
|
+
const isInline = variant === "inline";
|
|
298
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
299
|
+
"textarea",
|
|
300
|
+
{
|
|
301
|
+
ref,
|
|
302
|
+
disabled,
|
|
303
|
+
className: [
|
|
304
|
+
"w-full bg-transparent outline-none transition-colors resize-y",
|
|
305
|
+
isInline ? "" : "min-h-[96px] px-4 py-3 rounded-3xl",
|
|
306
|
+
"font-['Poppins'] text-sm placeholder:text-[var(--muted-fg)]",
|
|
307
|
+
!isInline && "border",
|
|
308
|
+
disabled ? "text-[#3F424F] cursor-not-allowed" + (isInline ? "" : " border-[#3F424F]") : [
|
|
309
|
+
"text-[var(--muted-fg)]",
|
|
310
|
+
isInline ? "" : "border-[var(--muted-fg)]",
|
|
311
|
+
isInline ? "" : "hover:border-[var(--border)]",
|
|
312
|
+
"focus:text-[var(--fg)]",
|
|
313
|
+
isInline ? "" : "focus:border-[var(--color-brand)]"
|
|
314
|
+
].join(" "),
|
|
315
|
+
className
|
|
316
|
+
].join(" "),
|
|
317
|
+
...props
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
Textarea.displayName = "Textarea";
|
|
295
323
|
function useIsomorphicLayoutEffect(effect, deps) {
|
|
296
|
-
const useEffectHook = typeof window !== "undefined" ?
|
|
324
|
+
const useEffectHook = typeof window !== "undefined" ? React3__namespace.useLayoutEffect : React3__namespace.useEffect;
|
|
297
325
|
useEffectHook(effect, deps);
|
|
298
326
|
}
|
|
299
327
|
function Modal({
|
|
@@ -305,9 +333,9 @@ function Modal({
|
|
|
305
333
|
zIndex = 50,
|
|
306
334
|
showDecorations = true
|
|
307
335
|
}) {
|
|
308
|
-
const [mounted, setMounted] =
|
|
309
|
-
const [isDark, setIsDark] =
|
|
310
|
-
|
|
336
|
+
const [mounted, setMounted] = React3__namespace.useState(false);
|
|
337
|
+
const [isDark, setIsDark] = React3__namespace.useState(false);
|
|
338
|
+
React3__namespace.useEffect(() => {
|
|
311
339
|
const checkDarkMode = () => {
|
|
312
340
|
const hasDarkClass = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark") || document.querySelector(".dark") !== null;
|
|
313
341
|
setIsDark(hasDarkClass);
|
|
@@ -330,7 +358,7 @@ function Modal({
|
|
|
330
358
|
document.body.style.overflow = original;
|
|
331
359
|
};
|
|
332
360
|
}, [open]);
|
|
333
|
-
|
|
361
|
+
React3__namespace.useEffect(() => {
|
|
334
362
|
if (!open) return;
|
|
335
363
|
const onKey = (e) => {
|
|
336
364
|
if (e.key === "Escape") onClose?.();
|
|
@@ -453,9 +481,9 @@ function Avatar({
|
|
|
453
481
|
style,
|
|
454
482
|
...props
|
|
455
483
|
}) {
|
|
456
|
-
const [imgError, setImgError] =
|
|
457
|
-
const initials =
|
|
458
|
-
const computedStyle =
|
|
484
|
+
const [imgError, setImgError] = React3.useState(false);
|
|
485
|
+
const initials = React3.useMemo(() => getInitials(name), [name]);
|
|
486
|
+
const computedStyle = React3.useMemo(() => {
|
|
459
487
|
const s = { ...style };
|
|
460
488
|
if (color) s.backgroundColor = color;
|
|
461
489
|
return s;
|
|
@@ -490,7 +518,7 @@ function AvatarGroup({
|
|
|
490
518
|
overlapPx = 8,
|
|
491
519
|
withRings = true
|
|
492
520
|
}) {
|
|
493
|
-
const avatars =
|
|
521
|
+
const avatars = React3__namespace.Children.toArray(children);
|
|
494
522
|
const displayAvatars = typeof max === "number" ? avatars.slice(0, max) : avatars;
|
|
495
523
|
const extraCount = typeof max === "number" && avatars.length > max ? avatars.length - max : 0;
|
|
496
524
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ["flex items-center", className].filter(Boolean).join(" "), children: [
|
|
@@ -591,7 +619,7 @@ function getIconButtonClasses(variant = "ghost", size = "md", className) {
|
|
|
591
619
|
};
|
|
592
620
|
return [base, variants[variant], sizes[size], className].filter(Boolean).join(" ");
|
|
593
621
|
}
|
|
594
|
-
var IconButton =
|
|
622
|
+
var IconButton = React3__namespace.forwardRef(
|
|
595
623
|
({
|
|
596
624
|
variant,
|
|
597
625
|
size,
|
|
@@ -638,25 +666,25 @@ function Autocomplete({
|
|
|
638
666
|
id,
|
|
639
667
|
...props
|
|
640
668
|
}) {
|
|
641
|
-
const inputId = id ??
|
|
669
|
+
const inputId = id ?? React3.useId();
|
|
642
670
|
const listboxId = `${inputId}-listbox`;
|
|
643
|
-
const [searchQuery, setSearchQuery] =
|
|
644
|
-
const [open, setOpen] =
|
|
645
|
-
const [activeIndex, setActiveIndex] =
|
|
646
|
-
const rootRef =
|
|
647
|
-
const listRef =
|
|
648
|
-
const normalizedOptions =
|
|
671
|
+
const [searchQuery, setSearchQuery] = React3.useState("");
|
|
672
|
+
const [open, setOpen] = React3.useState(false);
|
|
673
|
+
const [activeIndex, setActiveIndex] = React3.useState(-1);
|
|
674
|
+
const rootRef = React3.useRef(null);
|
|
675
|
+
const listRef = React3.useRef(null);
|
|
676
|
+
const normalizedOptions = React3.useMemo(() => {
|
|
649
677
|
if (Array.isArray(options) && typeof options[0] === "string") {
|
|
650
678
|
return options.map((str) => ({ label: str, value: str }));
|
|
651
679
|
}
|
|
652
680
|
return options;
|
|
653
681
|
}, [options]);
|
|
654
|
-
const filtered =
|
|
682
|
+
const filtered = React3.useMemo(() => {
|
|
655
683
|
const q = searchQuery.trim().toLowerCase();
|
|
656
684
|
if (!q) return normalizedOptions;
|
|
657
685
|
return normalizedOptions.filter((o) => o.label.toLowerCase().includes(q));
|
|
658
686
|
}, [normalizedOptions, searchQuery]);
|
|
659
|
-
const anyOptionHasImage =
|
|
687
|
+
const anyOptionHasImage = React3.useMemo(
|
|
660
688
|
() => normalizedOptions.some((o) => !!o.image),
|
|
661
689
|
[normalizedOptions]
|
|
662
690
|
);
|
|
@@ -848,27 +876,27 @@ function Search({
|
|
|
848
876
|
children,
|
|
849
877
|
...props
|
|
850
878
|
}) {
|
|
851
|
-
const inputId = id ??
|
|
879
|
+
const inputId = id ?? React3.useId();
|
|
852
880
|
const listboxId = `${inputId}-listbox`;
|
|
853
|
-
const [searchQuery, setSearchQuery] =
|
|
854
|
-
const [open, setOpen] =
|
|
855
|
-
const [activeIndex, setActiveIndex] =
|
|
856
|
-
const [filtersExpanded, setFiltersExpanded] =
|
|
857
|
-
const rootRef =
|
|
858
|
-
const listRef =
|
|
859
|
-
const searchTimeoutRef =
|
|
860
|
-
const normalizedOptions =
|
|
881
|
+
const [searchQuery, setSearchQuery] = React3.useState("");
|
|
882
|
+
const [open, setOpen] = React3.useState(false);
|
|
883
|
+
const [activeIndex, setActiveIndex] = React3.useState(-1);
|
|
884
|
+
const [filtersExpanded, setFiltersExpanded] = React3.useState(false);
|
|
885
|
+
const rootRef = React3.useRef(null);
|
|
886
|
+
const listRef = React3.useRef(null);
|
|
887
|
+
const searchTimeoutRef = React3.useRef(null);
|
|
888
|
+
const normalizedOptions = React3.useMemo(() => {
|
|
861
889
|
if (Array.isArray(options) && typeof options[0] === "string") {
|
|
862
890
|
return options.map((str) => ({ label: str, value: str }));
|
|
863
891
|
}
|
|
864
892
|
return options;
|
|
865
893
|
}, [options]);
|
|
866
|
-
|
|
894
|
+
React3.useMemo(
|
|
867
895
|
() => normalizedOptions.some((o) => !!o.image),
|
|
868
896
|
[normalizedOptions]
|
|
869
897
|
);
|
|
870
898
|
const displayValue = selectedOption != null ? typeof selectedOption === "string" ? selectedOption : selectedOption.label : searchQuery;
|
|
871
|
-
const debouncedSearch =
|
|
899
|
+
const debouncedSearch = React3.useCallback(
|
|
872
900
|
(query) => {
|
|
873
901
|
if (searchTimeoutRef.current) {
|
|
874
902
|
clearTimeout(searchTimeoutRef.current);
|
|
@@ -937,7 +965,7 @@ function Search({
|
|
|
937
965
|
const el = list.children[idx];
|
|
938
966
|
el?.scrollIntoView({ block: "nearest" });
|
|
939
967
|
}
|
|
940
|
-
|
|
968
|
+
React3__namespace.useEffect(() => {
|
|
941
969
|
return () => {
|
|
942
970
|
if (searchTimeoutRef.current) {
|
|
943
971
|
clearTimeout(searchTimeoutRef.current);
|
|
@@ -1044,7 +1072,7 @@ function getButtonClasses(variant = "primary", size = "md", fullWidth, className
|
|
|
1044
1072
|
className
|
|
1045
1073
|
].filter(Boolean).join(" ");
|
|
1046
1074
|
}
|
|
1047
|
-
var Button =
|
|
1075
|
+
var Button = React3__namespace.forwardRef(
|
|
1048
1076
|
({ variant, size, fullWidth, className, children, icon, ...props }, ref) => {
|
|
1049
1077
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1050
1078
|
"button",
|
|
@@ -1105,8 +1133,8 @@ function Counter({
|
|
|
1105
1133
|
className = "",
|
|
1106
1134
|
...props
|
|
1107
1135
|
}) {
|
|
1108
|
-
const [count, setCount] =
|
|
1109
|
-
|
|
1136
|
+
const [count, setCount] = React3__namespace.useState(value);
|
|
1137
|
+
React3__namespace.useEffect(() => {
|
|
1110
1138
|
setCount(value);
|
|
1111
1139
|
}, [value]);
|
|
1112
1140
|
const handleIncrement = () => {
|
|
@@ -1149,7 +1177,7 @@ function Counter({
|
|
|
1149
1177
|
] });
|
|
1150
1178
|
}
|
|
1151
1179
|
var AnimatedBgCircle = ({ colors, delay = 0 }) => {
|
|
1152
|
-
const uniqueId =
|
|
1180
|
+
const uniqueId = React3.useMemo(() => Math.random().toString(36).substr(2, 9), []);
|
|
1153
1181
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 64 64", fill: "none", className: "h-full transition-all duration-500 ease-in-out w-full", children: [
|
|
1154
1182
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
1155
1183
|
@keyframes colorCycle-${uniqueId} {
|
|
@@ -1181,7 +1209,7 @@ var AnimatedBgCircle = ({ colors, delay = 0 }) => {
|
|
|
1181
1209
|
};
|
|
1182
1210
|
var AnimatedBgCircle_default = AnimatedBgCircle;
|
|
1183
1211
|
var AnimatedBgRectangle = ({ colors, delay = 0 }) => {
|
|
1184
|
-
const uniqueId =
|
|
1212
|
+
const uniqueId = React3.useMemo(() => Math.random().toString(36).substr(2, 9), []);
|
|
1185
1213
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1186
1214
|
"svg",
|
|
1187
1215
|
{
|
|
@@ -1229,14 +1257,14 @@ var AgentButton = ({
|
|
|
1229
1257
|
logoAlt = "Agent",
|
|
1230
1258
|
animationColors = ["#7DADB9", "#3864F5", "#55468D", "#479A8D"]
|
|
1231
1259
|
}) => {
|
|
1232
|
-
const [showText, setShowText] =
|
|
1233
|
-
const [delayedHasNotification, setDelayedHasNotification] =
|
|
1234
|
-
const [isMounted, setIsMounted] =
|
|
1235
|
-
|
|
1260
|
+
const [showText, setShowText] = React3.useState(false);
|
|
1261
|
+
const [delayedHasNotification, setDelayedHasNotification] = React3.useState(false);
|
|
1262
|
+
const [isMounted, setIsMounted] = React3.useState(false);
|
|
1263
|
+
React3.useEffect(() => {
|
|
1236
1264
|
const timer = setTimeout(() => setIsMounted(true), 250);
|
|
1237
1265
|
return () => clearTimeout(timer);
|
|
1238
1266
|
}, []);
|
|
1239
|
-
|
|
1267
|
+
React3.useEffect(() => {
|
|
1240
1268
|
if (hasNotification) {
|
|
1241
1269
|
const textTimer = setTimeout(() => setShowText(true), 500);
|
|
1242
1270
|
setDelayedHasNotification(true);
|
|
@@ -1327,7 +1355,7 @@ var AgentButton = ({
|
|
|
1327
1355
|
);
|
|
1328
1356
|
};
|
|
1329
1357
|
var AgentButton_default = AgentButton;
|
|
1330
|
-
var MessageBubble =
|
|
1358
|
+
var MessageBubble = React3__namespace.forwardRef(
|
|
1331
1359
|
({ direction, color, children, className, ...props }, ref) => {
|
|
1332
1360
|
const borderRadiusClass = direction === "left" ? "[border-radius:16px_16px_16px_2px]" : direction === "right" ? "[border-radius:16px_16px_2px_16px]" : "rounded-2xl";
|
|
1333
1361
|
const backgroundColor = color || "var(--muted)";
|
|
@@ -1372,5 +1400,6 @@ exports.Modal = Modal;
|
|
|
1372
1400
|
exports.Search = Search;
|
|
1373
1401
|
exports.Separator = Separator;
|
|
1374
1402
|
exports.Skeleton = Skeleton;
|
|
1403
|
+
exports.Textarea = Textarea;
|
|
1375
1404
|
exports.Typo = Typo;
|
|
1376
1405
|
exports.assets = assets_exports;
|
package/dist/index.d.cts
CHANGED
|
@@ -67,6 +67,15 @@ declare const Input: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttribu
|
|
|
67
67
|
variant?: "default" | "inline";
|
|
68
68
|
} & React.RefAttributes<HTMLInputElement>>;
|
|
69
69
|
|
|
70
|
+
type TextareaProps = Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> & {
|
|
71
|
+
/** Textarea visual variant */
|
|
72
|
+
variant?: "default" | "inline";
|
|
73
|
+
};
|
|
74
|
+
declare const Textarea: React.ForwardRefExoticComponent<Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, "size"> & {
|
|
75
|
+
/** Textarea visual variant */
|
|
76
|
+
variant?: "default" | "inline";
|
|
77
|
+
} & React.RefAttributes<HTMLTextAreaElement>>;
|
|
78
|
+
|
|
70
79
|
type ModalProps = {
|
|
71
80
|
/** Whether the modal is open */
|
|
72
81
|
open: boolean;
|
|
@@ -322,4 +331,4 @@ type SeparatorProps = {
|
|
|
322
331
|
};
|
|
323
332
|
declare function Separator({ className }: SeparatorProps): react_jsx_runtime.JSX.Element;
|
|
324
333
|
|
|
325
|
-
export { AgentButton, type AgentButtonProps, AnimatedBgCircle, AnimatedBgRectangle, AppBackground, type AppBackgroundProps, Autocomplete, type AutocompleteOption, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BackgroundBlur, type BackgroundBlurProps, Button, type ButtonProps, Card, type CardProps, Chip, type ChipProps, Counter, type CounterProps, Icon, IconButton, type IconButtonProps, type IconProps, Input, type InputProps, MessageBubble, type MessageBubbleProps, Modal, type ModalProps, Search, type SearchOption, type SearchProps, Separator, type SeparatorProps, Skeleton, type SkeletonProps, Typo, type TypoProps, type TypoVariant, type TypoWeight, index as assets };
|
|
334
|
+
export { AgentButton, type AgentButtonProps, AnimatedBgCircle, AnimatedBgRectangle, AppBackground, type AppBackgroundProps, Autocomplete, type AutocompleteOption, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BackgroundBlur, type BackgroundBlurProps, Button, type ButtonProps, Card, type CardProps, Chip, type ChipProps, Counter, type CounterProps, Icon, IconButton, type IconButtonProps, type IconProps, Input, type InputProps, MessageBubble, type MessageBubbleProps, Modal, type ModalProps, Search, type SearchOption, type SearchProps, Separator, type SeparatorProps, Skeleton, type SkeletonProps, Textarea, type TextareaProps, Typo, type TypoProps, type TypoVariant, type TypoWeight, index as assets };
|
package/dist/index.d.ts
CHANGED
|
@@ -67,6 +67,15 @@ declare const Input: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttribu
|
|
|
67
67
|
variant?: "default" | "inline";
|
|
68
68
|
} & React.RefAttributes<HTMLInputElement>>;
|
|
69
69
|
|
|
70
|
+
type TextareaProps = Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> & {
|
|
71
|
+
/** Textarea visual variant */
|
|
72
|
+
variant?: "default" | "inline";
|
|
73
|
+
};
|
|
74
|
+
declare const Textarea: React.ForwardRefExoticComponent<Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, "size"> & {
|
|
75
|
+
/** Textarea visual variant */
|
|
76
|
+
variant?: "default" | "inline";
|
|
77
|
+
} & React.RefAttributes<HTMLTextAreaElement>>;
|
|
78
|
+
|
|
70
79
|
type ModalProps = {
|
|
71
80
|
/** Whether the modal is open */
|
|
72
81
|
open: boolean;
|
|
@@ -322,4 +331,4 @@ type SeparatorProps = {
|
|
|
322
331
|
};
|
|
323
332
|
declare function Separator({ className }: SeparatorProps): react_jsx_runtime.JSX.Element;
|
|
324
333
|
|
|
325
|
-
export { AgentButton, type AgentButtonProps, AnimatedBgCircle, AnimatedBgRectangle, AppBackground, type AppBackgroundProps, Autocomplete, type AutocompleteOption, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BackgroundBlur, type BackgroundBlurProps, Button, type ButtonProps, Card, type CardProps, Chip, type ChipProps, Counter, type CounterProps, Icon, IconButton, type IconButtonProps, type IconProps, Input, type InputProps, MessageBubble, type MessageBubbleProps, Modal, type ModalProps, Search, type SearchOption, type SearchProps, Separator, type SeparatorProps, Skeleton, type SkeletonProps, Typo, type TypoProps, type TypoVariant, type TypoWeight, index as assets };
|
|
334
|
+
export { AgentButton, type AgentButtonProps, AnimatedBgCircle, AnimatedBgRectangle, AppBackground, type AppBackgroundProps, Autocomplete, type AutocompleteOption, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BackgroundBlur, type BackgroundBlurProps, Button, type ButtonProps, Card, type CardProps, Chip, type ChipProps, Counter, type CounterProps, Icon, IconButton, type IconButtonProps, type IconProps, Input, type InputProps, MessageBubble, type MessageBubbleProps, Modal, type ModalProps, Search, type SearchOption, type SearchProps, Separator, type SeparatorProps, Skeleton, type SkeletonProps, Textarea, type TextareaProps, Typo, type TypoProps, type TypoVariant, type TypoWeight, index as assets };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import * as
|
|
2
|
+
import * as React3 from 'react';
|
|
3
3
|
import { useState, useEffect, useMemo, useId, useRef, useCallback } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
|
|
@@ -243,7 +243,7 @@ function Card({
|
|
|
243
243
|
}
|
|
244
244
|
);
|
|
245
245
|
}
|
|
246
|
-
var Input =
|
|
246
|
+
var Input = React3.forwardRef(
|
|
247
247
|
({ className, disabled, variant = "default", ...props }, ref) => {
|
|
248
248
|
const isInline = variant === "inline";
|
|
249
249
|
return /* @__PURE__ */ jsx(
|
|
@@ -254,7 +254,7 @@ var Input = React2.forwardRef(
|
|
|
254
254
|
className: [
|
|
255
255
|
"w-full bg-transparent outline-none transition-colors",
|
|
256
256
|
isInline ? "" : "h-12 px-4 rounded-full",
|
|
257
|
-
"text-sm placeholder:text-[var(--muted-fg)]",
|
|
257
|
+
"font-['Poppins'] text-sm placeholder:text-[var(--muted-fg)]",
|
|
258
258
|
!isInline && "border",
|
|
259
259
|
disabled ? "text-[#3F424F] cursor-not-allowed" + (isInline ? "" : " border-[#3F424F]") : [
|
|
260
260
|
"text-[var(--muted-fg)]",
|
|
@@ -271,8 +271,36 @@ var Input = React2.forwardRef(
|
|
|
271
271
|
}
|
|
272
272
|
);
|
|
273
273
|
Input.displayName = "Input";
|
|
274
|
+
var Textarea = React3.forwardRef(
|
|
275
|
+
({ className, disabled, variant = "default", ...props }, ref) => {
|
|
276
|
+
const isInline = variant === "inline";
|
|
277
|
+
return /* @__PURE__ */ jsx(
|
|
278
|
+
"textarea",
|
|
279
|
+
{
|
|
280
|
+
ref,
|
|
281
|
+
disabled,
|
|
282
|
+
className: [
|
|
283
|
+
"w-full bg-transparent outline-none transition-colors resize-y",
|
|
284
|
+
isInline ? "" : "min-h-[96px] px-4 py-3 rounded-3xl",
|
|
285
|
+
"font-['Poppins'] text-sm placeholder:text-[var(--muted-fg)]",
|
|
286
|
+
!isInline && "border",
|
|
287
|
+
disabled ? "text-[#3F424F] cursor-not-allowed" + (isInline ? "" : " border-[#3F424F]") : [
|
|
288
|
+
"text-[var(--muted-fg)]",
|
|
289
|
+
isInline ? "" : "border-[var(--muted-fg)]",
|
|
290
|
+
isInline ? "" : "hover:border-[var(--border)]",
|
|
291
|
+
"focus:text-[var(--fg)]",
|
|
292
|
+
isInline ? "" : "focus:border-[var(--color-brand)]"
|
|
293
|
+
].join(" "),
|
|
294
|
+
className
|
|
295
|
+
].join(" "),
|
|
296
|
+
...props
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
Textarea.displayName = "Textarea";
|
|
274
302
|
function useIsomorphicLayoutEffect(effect, deps) {
|
|
275
|
-
const useEffectHook = typeof window !== "undefined" ?
|
|
303
|
+
const useEffectHook = typeof window !== "undefined" ? React3.useLayoutEffect : React3.useEffect;
|
|
276
304
|
useEffectHook(effect, deps);
|
|
277
305
|
}
|
|
278
306
|
function Modal({
|
|
@@ -284,9 +312,9 @@ function Modal({
|
|
|
284
312
|
zIndex = 50,
|
|
285
313
|
showDecorations = true
|
|
286
314
|
}) {
|
|
287
|
-
const [mounted, setMounted] =
|
|
288
|
-
const [isDark, setIsDark] =
|
|
289
|
-
|
|
315
|
+
const [mounted, setMounted] = React3.useState(false);
|
|
316
|
+
const [isDark, setIsDark] = React3.useState(false);
|
|
317
|
+
React3.useEffect(() => {
|
|
290
318
|
const checkDarkMode = () => {
|
|
291
319
|
const hasDarkClass = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark") || document.querySelector(".dark") !== null;
|
|
292
320
|
setIsDark(hasDarkClass);
|
|
@@ -309,7 +337,7 @@ function Modal({
|
|
|
309
337
|
document.body.style.overflow = original;
|
|
310
338
|
};
|
|
311
339
|
}, [open]);
|
|
312
|
-
|
|
340
|
+
React3.useEffect(() => {
|
|
313
341
|
if (!open) return;
|
|
314
342
|
const onKey = (e) => {
|
|
315
343
|
if (e.key === "Escape") onClose?.();
|
|
@@ -469,7 +497,7 @@ function AvatarGroup({
|
|
|
469
497
|
overlapPx = 8,
|
|
470
498
|
withRings = true
|
|
471
499
|
}) {
|
|
472
|
-
const avatars =
|
|
500
|
+
const avatars = React3.Children.toArray(children);
|
|
473
501
|
const displayAvatars = typeof max === "number" ? avatars.slice(0, max) : avatars;
|
|
474
502
|
const extraCount = typeof max === "number" && avatars.length > max ? avatars.length - max : 0;
|
|
475
503
|
return /* @__PURE__ */ jsxs("div", { className: ["flex items-center", className].filter(Boolean).join(" "), children: [
|
|
@@ -570,7 +598,7 @@ function getIconButtonClasses(variant = "ghost", size = "md", className) {
|
|
|
570
598
|
};
|
|
571
599
|
return [base, variants[variant], sizes[size], className].filter(Boolean).join(" ");
|
|
572
600
|
}
|
|
573
|
-
var IconButton =
|
|
601
|
+
var IconButton = React3.forwardRef(
|
|
574
602
|
({
|
|
575
603
|
variant,
|
|
576
604
|
size,
|
|
@@ -916,7 +944,7 @@ function Search({
|
|
|
916
944
|
const el = list.children[idx];
|
|
917
945
|
el?.scrollIntoView({ block: "nearest" });
|
|
918
946
|
}
|
|
919
|
-
|
|
947
|
+
React3.useEffect(() => {
|
|
920
948
|
return () => {
|
|
921
949
|
if (searchTimeoutRef.current) {
|
|
922
950
|
clearTimeout(searchTimeoutRef.current);
|
|
@@ -1023,7 +1051,7 @@ function getButtonClasses(variant = "primary", size = "md", fullWidth, className
|
|
|
1023
1051
|
className
|
|
1024
1052
|
].filter(Boolean).join(" ");
|
|
1025
1053
|
}
|
|
1026
|
-
var Button =
|
|
1054
|
+
var Button = React3.forwardRef(
|
|
1027
1055
|
({ variant, size, fullWidth, className, children, icon, ...props }, ref) => {
|
|
1028
1056
|
return /* @__PURE__ */ jsx(
|
|
1029
1057
|
"button",
|
|
@@ -1084,8 +1112,8 @@ function Counter({
|
|
|
1084
1112
|
className = "",
|
|
1085
1113
|
...props
|
|
1086
1114
|
}) {
|
|
1087
|
-
const [count, setCount] =
|
|
1088
|
-
|
|
1115
|
+
const [count, setCount] = React3.useState(value);
|
|
1116
|
+
React3.useEffect(() => {
|
|
1089
1117
|
setCount(value);
|
|
1090
1118
|
}, [value]);
|
|
1091
1119
|
const handleIncrement = () => {
|
|
@@ -1306,7 +1334,7 @@ var AgentButton = ({
|
|
|
1306
1334
|
);
|
|
1307
1335
|
};
|
|
1308
1336
|
var AgentButton_default = AgentButton;
|
|
1309
|
-
var MessageBubble =
|
|
1337
|
+
var MessageBubble = React3.forwardRef(
|
|
1310
1338
|
({ direction, color, children, className, ...props }, ref) => {
|
|
1311
1339
|
const borderRadiusClass = direction === "left" ? "[border-radius:16px_16px_16px_2px]" : direction === "right" ? "[border-radius:16px_16px_2px_16px]" : "rounded-2xl";
|
|
1312
1340
|
const backgroundColor = color || "var(--muted)";
|
|
@@ -1331,4 +1359,4 @@ function Separator({ className = "" }) {
|
|
|
1331
1359
|
return /* @__PURE__ */ jsx("div", { className: `w-full my-1.5 h-px bg-[var(--border)] ${className}` });
|
|
1332
1360
|
}
|
|
1333
1361
|
|
|
1334
|
-
export { AgentButton_default as AgentButton, AnimatedBgCircle_default as AnimatedBgCircle, AnimatedBgRectangle_default as AnimatedBgRectangle, AppBackground, Autocomplete, Avatar, AvatarGroup, BackgroundBlur, Button, Card, Chip, Counter, Icon, IconButton, Input, MessageBubble, Modal, Search, Separator, Skeleton, Typo, assets_exports as assets };
|
|
1362
|
+
export { AgentButton_default as AgentButton, AnimatedBgCircle_default as AnimatedBgCircle, AnimatedBgRectangle_default as AnimatedBgRectangle, AppBackground, Autocomplete, Avatar, AvatarGroup, BackgroundBlur, Button, Card, Chip, Counter, Icon, IconButton, Input, MessageBubble, Modal, Search, Separator, Skeleton, Textarea, Typo, assets_exports as assets };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neoptocom/neopto-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A modern React component library built with Tailwind CSS v4 and TypeScript. Features dark mode, design tokens, and comprehensive Storybook documentation. Requires Tailwind v4+.",
|
|
6
6
|
"keywords": [
|
package/src/components/Input.tsx
CHANGED
|
@@ -16,7 +16,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
16
16
|
className={[
|
|
17
17
|
"w-full bg-transparent outline-none transition-colors",
|
|
18
18
|
isInline ? "" : "h-12 px-4 rounded-full",
|
|
19
|
-
"text-sm placeholder:text-[var(--muted-fg)]",
|
|
19
|
+
"font-['Poppins'] text-sm placeholder:text-[var(--muted-fg)]",
|
|
20
20
|
!isInline && "border",
|
|
21
21
|
disabled
|
|
22
22
|
? "text-[#3F424F] cursor-not-allowed" + (isInline ? "" : " border-[#3F424F]")
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
export type TextareaProps = Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'> & {
|
|
4
|
+
/** Textarea visual variant */
|
|
5
|
+
variant?: "default" | "inline";
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
9
|
+
({ className, disabled, variant = "default", ...props }, ref) => {
|
|
10
|
+
const isInline = variant === "inline";
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<textarea
|
|
14
|
+
ref={ref}
|
|
15
|
+
disabled={disabled}
|
|
16
|
+
className={[
|
|
17
|
+
"w-full bg-transparent outline-none transition-colors resize-y",
|
|
18
|
+
isInline ? "" : "min-h-[96px] px-4 py-3 rounded-3xl",
|
|
19
|
+
"font-['Poppins'] text-sm placeholder:text-[var(--muted-fg)]",
|
|
20
|
+
!isInline && "border",
|
|
21
|
+
disabled
|
|
22
|
+
? "text-[#3F424F] cursor-not-allowed" + (isInline ? "" : " border-[#3F424F]")
|
|
23
|
+
: [
|
|
24
|
+
"text-[var(--muted-fg)]",
|
|
25
|
+
isInline ? "" : "border-[var(--muted-fg)]",
|
|
26
|
+
isInline ? "" : "hover:border-[var(--border)]",
|
|
27
|
+
"focus:text-[var(--fg)]",
|
|
28
|
+
isInline ? "" : "focus:border-[var(--color-brand)]"
|
|
29
|
+
].join(" "),
|
|
30
|
+
className
|
|
31
|
+
].join(" ")}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
Textarea.displayName = "Textarea";
|
|
38
|
+
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { AppBackground } from "./components/AppBackground";
|
|
|
6
6
|
export { BackgroundBlur } from "./components/BackgroundBlur";
|
|
7
7
|
export { Card } from "./components/Card";
|
|
8
8
|
export * from "./components/Input";
|
|
9
|
+
export * from "./components/Textarea";
|
|
9
10
|
export * from "./components/Modal";
|
|
10
11
|
export { default as Typo } from "./components/Typo";
|
|
11
12
|
export { default as Avatar } from "./components/Avatar";
|
|
@@ -27,6 +28,7 @@ export type { AppBackgroundProps } from "./components/AppBackground";
|
|
|
27
28
|
export type { BackgroundBlurProps } from "./components/BackgroundBlur";
|
|
28
29
|
export type { CardProps } from "./components/Card";
|
|
29
30
|
export type { InputProps } from "./components/Input";
|
|
31
|
+
export type { TextareaProps } from "./components/Textarea";
|
|
30
32
|
export type { ModalProps } from "./components/Modal";
|
|
31
33
|
export type { TypoProps, TypoVariant, TypoWeight } from "./components/Typo";
|
|
32
34
|
export type { AvatarProps } from "./components/Avatar";
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Textarea } from "../components/Textarea";
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Textarea> = {
|
|
5
|
+
title: "Components/Textarea",
|
|
6
|
+
component: Textarea,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: "padded",
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof Textarea>;
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
render: () => (
|
|
16
|
+
<div className="flex flex-col gap-4 w-96">
|
|
17
|
+
<Textarea placeholder="Enter your message..." />
|
|
18
|
+
<Textarea placeholder="With default value..." defaultValue="This is some default text that spans multiple lines and shows how the textarea handles content." />
|
|
19
|
+
</div>
|
|
20
|
+
),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Inline: Story = {
|
|
24
|
+
render: () => (
|
|
25
|
+
<div className="flex flex-col gap-4 w-96">
|
|
26
|
+
<Textarea variant="inline" placeholder="Inline textarea" />
|
|
27
|
+
<Textarea variant="inline" placeholder="Inline with value" defaultValue="Inline textarea with some content" />
|
|
28
|
+
<Textarea variant="inline" placeholder="Disabled inline" disabled />
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const States: Story = {
|
|
34
|
+
render: () => (
|
|
35
|
+
<div className="flex flex-col gap-4 w-96">
|
|
36
|
+
<div>
|
|
37
|
+
<label className="block text-sm font-medium mb-2">Default</label>
|
|
38
|
+
<Textarea placeholder="Type something..." />
|
|
39
|
+
</div>
|
|
40
|
+
<div>
|
|
41
|
+
<label className="block text-sm font-medium mb-2">With Value</label>
|
|
42
|
+
<Textarea defaultValue="This textarea has some content already filled in." />
|
|
43
|
+
</div>
|
|
44
|
+
<div>
|
|
45
|
+
<label className="block text-sm font-medium mb-2">Disabled</label>
|
|
46
|
+
<Textarea placeholder="Disabled textarea" disabled />
|
|
47
|
+
</div>
|
|
48
|
+
<div>
|
|
49
|
+
<label className="block text-sm font-medium mb-2">Disabled with Value</label>
|
|
50
|
+
<Textarea defaultValue="Disabled with content" disabled />
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const Sizes: Story = {
|
|
57
|
+
render: () => (
|
|
58
|
+
<div className="flex flex-col gap-4 w-96">
|
|
59
|
+
<div>
|
|
60
|
+
<label className="block text-sm font-medium mb-2">Small (3 rows)</label>
|
|
61
|
+
<Textarea placeholder="Small textarea..." rows={3} />
|
|
62
|
+
</div>
|
|
63
|
+
<div>
|
|
64
|
+
<label className="block text-sm font-medium mb-2">Medium (5 rows)</label>
|
|
65
|
+
<Textarea placeholder="Medium textarea..." rows={5} />
|
|
66
|
+
</div>
|
|
67
|
+
<div>
|
|
68
|
+
<label className="block text-sm font-medium mb-2">Large (8 rows)</label>
|
|
69
|
+
<Textarea placeholder="Large textarea..." rows={8} />
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const WithCustomStyling: Story = {
|
|
76
|
+
render: () => (
|
|
77
|
+
<div className="flex flex-col gap-4 w-96">
|
|
78
|
+
<Textarea
|
|
79
|
+
placeholder="Custom height..."
|
|
80
|
+
style={{ minHeight: "200px" }}
|
|
81
|
+
/>
|
|
82
|
+
<Textarea
|
|
83
|
+
placeholder="Fixed height (no resize)..."
|
|
84
|
+
className="resize-none"
|
|
85
|
+
rows={4}
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
|