@neoptocom/neopto-ui 1.5.4 → 1.6.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 +351 -50
- package/dist/index.d.cts +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +316 -17
- package/package.json +1 -1
- package/src/components/Autocomplete.tsx +1 -1
- package/src/components/Calendar.tsx +217 -0
- package/src/components/DateInput.tsx +225 -0
- package/src/index.ts +5 -1
- package/src/stories/DateInput.stories.tsx +136 -0
package/dist/index.d.ts
CHANGED
|
@@ -392,4 +392,51 @@ interface BreadcrumbProps {
|
|
|
392
392
|
*/
|
|
393
393
|
declare const Breadcrumb: React__default.FC<BreadcrumbProps>;
|
|
394
394
|
|
|
395
|
-
|
|
395
|
+
type DateInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "onChange" | "type"> & {
|
|
396
|
+
/** Label text displayed above the input */
|
|
397
|
+
label?: string;
|
|
398
|
+
/** Current date value */
|
|
399
|
+
value?: Date | null;
|
|
400
|
+
/** Callback when date changes */
|
|
401
|
+
onChange: (date: Date) => void;
|
|
402
|
+
/** Flag to visually mark the input as errored */
|
|
403
|
+
error?: boolean;
|
|
404
|
+
/** Minimum selectable date */
|
|
405
|
+
minDate?: Date;
|
|
406
|
+
/** Maximum selectable date */
|
|
407
|
+
maxDate?: Date;
|
|
408
|
+
/** Placeholder text (default: "00/00/0000") */
|
|
409
|
+
placeholder?: string;
|
|
410
|
+
};
|
|
411
|
+
declare const DateInput: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange" | "type" | "value"> & {
|
|
412
|
+
/** Label text displayed above the input */
|
|
413
|
+
label?: string;
|
|
414
|
+
/** Current date value */
|
|
415
|
+
value?: Date | null;
|
|
416
|
+
/** Callback when date changes */
|
|
417
|
+
onChange: (date: Date) => void;
|
|
418
|
+
/** Flag to visually mark the input as errored */
|
|
419
|
+
error?: boolean;
|
|
420
|
+
/** Minimum selectable date */
|
|
421
|
+
minDate?: Date;
|
|
422
|
+
/** Maximum selectable date */
|
|
423
|
+
maxDate?: Date;
|
|
424
|
+
/** Placeholder text (default: "00/00/0000") */
|
|
425
|
+
placeholder?: string;
|
|
426
|
+
} & React.RefAttributes<HTMLInputElement>>;
|
|
427
|
+
|
|
428
|
+
type CalendarProps = {
|
|
429
|
+
/** Currently selected date */
|
|
430
|
+
selectedDate?: Date;
|
|
431
|
+
/** Callback when a date is selected */
|
|
432
|
+
onDateSelect: (date: Date) => void;
|
|
433
|
+
/** Today's date (for highlighting) */
|
|
434
|
+
today?: Date;
|
|
435
|
+
/** Minimum selectable date */
|
|
436
|
+
minDate?: Date;
|
|
437
|
+
/** Maximum selectable date */
|
|
438
|
+
maxDate?: Date;
|
|
439
|
+
};
|
|
440
|
+
declare function Calendar({ selectedDate, onDateSelect, today, minDate, maxDate, }: CalendarProps): react_jsx_runtime.JSX.Element;
|
|
441
|
+
|
|
442
|
+
export { AgentButton, type AgentButtonProps, AnimatedBgCircle, AnimatedBgRectangle, AppBackground, type AppBackgroundProps, Autocomplete, type AutocompleteOption, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BackgroundBlur, type BackgroundBlurProps, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Button, type ButtonProps, Calendar, type CalendarProps, Card, type CardProps, Chip, type ChipProps, Counter, type CounterProps, DateInput, type DateInputProps, 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 React11 from 'react';
|
|
3
3
|
import { useState, useEffect, useMemo, useId, useRef, useCallback } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
|
|
@@ -276,7 +276,7 @@ function Icon({
|
|
|
276
276
|
}
|
|
277
277
|
);
|
|
278
278
|
}
|
|
279
|
-
var Input =
|
|
279
|
+
var Input = React11.forwardRef(
|
|
280
280
|
({
|
|
281
281
|
className,
|
|
282
282
|
disabled,
|
|
@@ -369,7 +369,7 @@ var Input = React3.forwardRef(
|
|
|
369
369
|
}
|
|
370
370
|
);
|
|
371
371
|
Input.displayName = "Input";
|
|
372
|
-
var Textarea =
|
|
372
|
+
var Textarea = React11.forwardRef(
|
|
373
373
|
({ className, disabled, variant = "default", ...props }, ref) => {
|
|
374
374
|
const isInline = variant === "inline";
|
|
375
375
|
return /* @__PURE__ */ jsx(
|
|
@@ -398,7 +398,7 @@ var Textarea = React3.forwardRef(
|
|
|
398
398
|
);
|
|
399
399
|
Textarea.displayName = "Textarea";
|
|
400
400
|
function useIsomorphicLayoutEffect(effect, deps) {
|
|
401
|
-
const useEffectHook = typeof window !== "undefined" ?
|
|
401
|
+
const useEffectHook = typeof window !== "undefined" ? React11.useLayoutEffect : React11.useEffect;
|
|
402
402
|
useEffectHook(effect, deps);
|
|
403
403
|
}
|
|
404
404
|
function Modal({
|
|
@@ -410,9 +410,9 @@ function Modal({
|
|
|
410
410
|
zIndex = 50,
|
|
411
411
|
showDecorations = true
|
|
412
412
|
}) {
|
|
413
|
-
const [mounted, setMounted] =
|
|
414
|
-
const [isDark, setIsDark] =
|
|
415
|
-
|
|
413
|
+
const [mounted, setMounted] = React11.useState(false);
|
|
414
|
+
const [isDark, setIsDark] = React11.useState(false);
|
|
415
|
+
React11.useEffect(() => {
|
|
416
416
|
const checkDarkMode = () => {
|
|
417
417
|
const hasDarkClass = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark") || document.querySelector(".dark") !== null;
|
|
418
418
|
setIsDark(hasDarkClass);
|
|
@@ -435,7 +435,7 @@ function Modal({
|
|
|
435
435
|
document.body.style.overflow = original;
|
|
436
436
|
};
|
|
437
437
|
}, [open]);
|
|
438
|
-
|
|
438
|
+
React11.useEffect(() => {
|
|
439
439
|
if (!open) return;
|
|
440
440
|
const onKey = (e) => {
|
|
441
441
|
if (e.key === "Escape") onClose?.();
|
|
@@ -595,7 +595,7 @@ function AvatarGroup({
|
|
|
595
595
|
overlapPx = 8,
|
|
596
596
|
withRings = true
|
|
597
597
|
}) {
|
|
598
|
-
const avatars =
|
|
598
|
+
const avatars = React11.Children.toArray(children);
|
|
599
599
|
const displayAvatars = typeof max === "number" ? avatars.slice(0, max) : avatars;
|
|
600
600
|
const extraCount = typeof max === "number" && avatars.length > max ? avatars.length - max : 0;
|
|
601
601
|
return /* @__PURE__ */ jsxs("div", { className: ["flex items-center", className].filter(Boolean).join(" "), children: [
|
|
@@ -663,7 +663,7 @@ function getIconButtonClasses(variant = "ghost", size = "md", className) {
|
|
|
663
663
|
};
|
|
664
664
|
return [base, variants[variant], sizes[size], className].filter(Boolean).join(" ");
|
|
665
665
|
}
|
|
666
|
-
var IconButton =
|
|
666
|
+
var IconButton = React11.forwardRef(
|
|
667
667
|
({
|
|
668
668
|
variant,
|
|
669
669
|
size,
|
|
@@ -822,7 +822,7 @@ function Autocomplete({
|
|
|
822
822
|
"fieldset",
|
|
823
823
|
{
|
|
824
824
|
className: [
|
|
825
|
-
"w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-
|
|
825
|
+
"w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-14",
|
|
826
826
|
"border-[var(--border)] focus-within:border-[var(--color-brand)]",
|
|
827
827
|
disabled ? "opacity-60 cursor-not-allowed" : ""
|
|
828
828
|
].join(" "),
|
|
@@ -1037,7 +1037,7 @@ function Search({
|
|
|
1037
1037
|
const el = list.children[idx];
|
|
1038
1038
|
el?.scrollIntoView({ block: "nearest" });
|
|
1039
1039
|
}
|
|
1040
|
-
|
|
1040
|
+
React11.useEffect(() => {
|
|
1041
1041
|
return () => {
|
|
1042
1042
|
if (searchTimeoutRef.current) {
|
|
1043
1043
|
clearTimeout(searchTimeoutRef.current);
|
|
@@ -1144,7 +1144,7 @@ function getButtonClasses(variant = "primary", size = "md", fullWidth, className
|
|
|
1144
1144
|
className
|
|
1145
1145
|
].filter(Boolean).join(" ");
|
|
1146
1146
|
}
|
|
1147
|
-
var Button =
|
|
1147
|
+
var Button = React11.forwardRef(
|
|
1148
1148
|
({ variant, size, fullWidth, className, children, icon, ...props }, ref) => {
|
|
1149
1149
|
return /* @__PURE__ */ jsx(
|
|
1150
1150
|
"button",
|
|
@@ -1216,8 +1216,8 @@ function Counter({
|
|
|
1216
1216
|
className = "",
|
|
1217
1217
|
...props
|
|
1218
1218
|
}) {
|
|
1219
|
-
const [count, setCount] =
|
|
1220
|
-
|
|
1219
|
+
const [count, setCount] = React11.useState(value);
|
|
1220
|
+
React11.useEffect(() => {
|
|
1221
1221
|
setCount(value);
|
|
1222
1222
|
}, [value]);
|
|
1223
1223
|
const handleIncrement = () => {
|
|
@@ -1450,7 +1450,7 @@ var AgentButton = ({
|
|
|
1450
1450
|
);
|
|
1451
1451
|
};
|
|
1452
1452
|
var AgentButton_default = AgentButton;
|
|
1453
|
-
var MessageBubble =
|
|
1453
|
+
var MessageBubble = React11.forwardRef(
|
|
1454
1454
|
({ direction, color, children, className, ...props }, ref) => {
|
|
1455
1455
|
const borderRadiusClass = direction === "left" ? "[border-radius:16px_16px_16px_2px]" : direction === "right" ? "[border-radius:16px_16px_2px_16px]" : "rounded-2xl";
|
|
1456
1456
|
const backgroundColor = color || "var(--muted)";
|
|
@@ -1528,5 +1528,304 @@ var Breadcrumb = ({
|
|
|
1528
1528
|
] }, index);
|
|
1529
1529
|
}) }) });
|
|
1530
1530
|
};
|
|
1531
|
+
var DAYS_OF_WEEK = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
1532
|
+
var MONTHS = [
|
|
1533
|
+
"January",
|
|
1534
|
+
"February",
|
|
1535
|
+
"March",
|
|
1536
|
+
"April",
|
|
1537
|
+
"May",
|
|
1538
|
+
"June",
|
|
1539
|
+
"July",
|
|
1540
|
+
"August",
|
|
1541
|
+
"September",
|
|
1542
|
+
"October",
|
|
1543
|
+
"November",
|
|
1544
|
+
"December"
|
|
1545
|
+
];
|
|
1546
|
+
function isSameDay(date1, date2) {
|
|
1547
|
+
return date1.getDate() === date2.getDate() && date1.getMonth() === date2.getMonth() && date1.getFullYear() === date2.getFullYear();
|
|
1548
|
+
}
|
|
1549
|
+
function isSameMonth(date1, date2) {
|
|
1550
|
+
return date1.getMonth() === date2.getMonth() && date1.getFullYear() === date2.getFullYear();
|
|
1551
|
+
}
|
|
1552
|
+
function startOfDay(date) {
|
|
1553
|
+
const d = new Date(date);
|
|
1554
|
+
d.setHours(0, 0, 0, 0);
|
|
1555
|
+
return d;
|
|
1556
|
+
}
|
|
1557
|
+
function Calendar({
|
|
1558
|
+
selectedDate,
|
|
1559
|
+
onDateSelect,
|
|
1560
|
+
today = /* @__PURE__ */ new Date(),
|
|
1561
|
+
minDate,
|
|
1562
|
+
maxDate
|
|
1563
|
+
}) {
|
|
1564
|
+
const [currentMonth, setCurrentMonth] = React11.useState(
|
|
1565
|
+
selectedDate || /* @__PURE__ */ new Date()
|
|
1566
|
+
);
|
|
1567
|
+
const todayStart = startOfDay(today);
|
|
1568
|
+
const selectedDateStart = selectedDate ? startOfDay(selectedDate) : null;
|
|
1569
|
+
const firstDayOfMonth = new Date(
|
|
1570
|
+
currentMonth.getFullYear(),
|
|
1571
|
+
currentMonth.getMonth(),
|
|
1572
|
+
1
|
|
1573
|
+
);
|
|
1574
|
+
const lastDayOfMonth = new Date(
|
|
1575
|
+
currentMonth.getFullYear(),
|
|
1576
|
+
currentMonth.getMonth() + 1,
|
|
1577
|
+
0
|
|
1578
|
+
);
|
|
1579
|
+
const daysInMonth = lastDayOfMonth.getDate();
|
|
1580
|
+
const startingDayOfWeek = firstDayOfMonth.getDay();
|
|
1581
|
+
const prevMonth = () => {
|
|
1582
|
+
setCurrentMonth(
|
|
1583
|
+
new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)
|
|
1584
|
+
);
|
|
1585
|
+
};
|
|
1586
|
+
const nextMonth = () => {
|
|
1587
|
+
setCurrentMonth(
|
|
1588
|
+
new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)
|
|
1589
|
+
);
|
|
1590
|
+
};
|
|
1591
|
+
const handleDateClick = (day) => {
|
|
1592
|
+
const date = new Date(
|
|
1593
|
+
currentMonth.getFullYear(),
|
|
1594
|
+
currentMonth.getMonth(),
|
|
1595
|
+
day
|
|
1596
|
+
);
|
|
1597
|
+
const dateStart = startOfDay(date);
|
|
1598
|
+
if (minDate && dateStart < startOfDay(minDate)) return;
|
|
1599
|
+
if (maxDate && dateStart > startOfDay(maxDate)) return;
|
|
1600
|
+
onDateSelect(date);
|
|
1601
|
+
};
|
|
1602
|
+
const isDateDisabled = (day) => {
|
|
1603
|
+
const date = new Date(
|
|
1604
|
+
currentMonth.getFullYear(),
|
|
1605
|
+
currentMonth.getMonth(),
|
|
1606
|
+
day
|
|
1607
|
+
);
|
|
1608
|
+
const dateStart = startOfDay(date);
|
|
1609
|
+
if (minDate && dateStart < startOfDay(minDate)) return true;
|
|
1610
|
+
if (maxDate && dateStart > startOfDay(maxDate)) return true;
|
|
1611
|
+
return false;
|
|
1612
|
+
};
|
|
1613
|
+
const days = [];
|
|
1614
|
+
for (let i = 0; i < startingDayOfWeek; i++) {
|
|
1615
|
+
days.push(null);
|
|
1616
|
+
}
|
|
1617
|
+
for (let day = 1; day <= daysInMonth; day++) {
|
|
1618
|
+
days.push(day);
|
|
1619
|
+
}
|
|
1620
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
|
|
1621
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
1622
|
+
/* @__PURE__ */ jsx(
|
|
1623
|
+
"button",
|
|
1624
|
+
{
|
|
1625
|
+
type: "button",
|
|
1626
|
+
onClick: prevMonth,
|
|
1627
|
+
className: "p-2 rounded-full hover:bg-[var(--muted)] transition-colors",
|
|
1628
|
+
"aria-label": "Previous month",
|
|
1629
|
+
children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded text-[var(--fg)]", children: "chevron_left" })
|
|
1630
|
+
}
|
|
1631
|
+
),
|
|
1632
|
+
/* @__PURE__ */ jsxs("h3", { className: "text-sm font-medium text-[var(--fg)]", children: [
|
|
1633
|
+
MONTHS[currentMonth.getMonth()],
|
|
1634
|
+
" ",
|
|
1635
|
+
currentMonth.getFullYear()
|
|
1636
|
+
] }),
|
|
1637
|
+
/* @__PURE__ */ jsx(
|
|
1638
|
+
"button",
|
|
1639
|
+
{
|
|
1640
|
+
type: "button",
|
|
1641
|
+
onClick: nextMonth,
|
|
1642
|
+
className: "p-2 rounded-full hover:bg-[var(--muted)] transition-colors",
|
|
1643
|
+
"aria-label": "Next month",
|
|
1644
|
+
children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded text-[var(--fg)]", children: "chevron_right" })
|
|
1645
|
+
}
|
|
1646
|
+
)
|
|
1647
|
+
] }),
|
|
1648
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1 mb-2", children: DAYS_OF_WEEK.map((day) => /* @__PURE__ */ jsx(
|
|
1649
|
+
"div",
|
|
1650
|
+
{
|
|
1651
|
+
className: "text-xs text-center text-[var(--muted-fg)] font-medium py-1",
|
|
1652
|
+
children: day
|
|
1653
|
+
},
|
|
1654
|
+
day
|
|
1655
|
+
)) }),
|
|
1656
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((day, idx) => {
|
|
1657
|
+
if (day === null) {
|
|
1658
|
+
return /* @__PURE__ */ jsx("div", { className: "aspect-square" }, `empty-${idx}`);
|
|
1659
|
+
}
|
|
1660
|
+
const date = new Date(
|
|
1661
|
+
currentMonth.getFullYear(),
|
|
1662
|
+
currentMonth.getMonth(),
|
|
1663
|
+
day
|
|
1664
|
+
);
|
|
1665
|
+
const dateStart = startOfDay(date);
|
|
1666
|
+
const isSelected = selectedDateStart && isSameDay(dateStart, selectedDateStart);
|
|
1667
|
+
const isToday = isSameDay(dateStart, todayStart);
|
|
1668
|
+
const isDisabled = isDateDisabled(day);
|
|
1669
|
+
const isCurrentMonth = isSameMonth(dateStart, currentMonth);
|
|
1670
|
+
return /* @__PURE__ */ jsx(
|
|
1671
|
+
"button",
|
|
1672
|
+
{
|
|
1673
|
+
type: "button",
|
|
1674
|
+
onClick: () => handleDateClick(day),
|
|
1675
|
+
disabled: isDisabled,
|
|
1676
|
+
className: [
|
|
1677
|
+
"aspect-square rounded-lg text-sm transition-colors",
|
|
1678
|
+
isSelected ? "bg-[var(--color-brand)] text-white font-medium" : isToday ? "bg-[var(--muted)] text-[var(--fg)] font-medium border border-[var(--color-brand)]" : "text-[var(--fg)] hover:bg-[var(--muted)]",
|
|
1679
|
+
isDisabled ? "opacity-30 cursor-not-allowed" : "cursor-pointer",
|
|
1680
|
+
!isCurrentMonth ? "opacity-50" : ""
|
|
1681
|
+
].filter(Boolean).join(" "),
|
|
1682
|
+
children: day
|
|
1683
|
+
},
|
|
1684
|
+
day
|
|
1685
|
+
);
|
|
1686
|
+
}) })
|
|
1687
|
+
] });
|
|
1688
|
+
}
|
|
1689
|
+
function formatDate(date) {
|
|
1690
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
1691
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
1692
|
+
const year = date.getFullYear();
|
|
1693
|
+
return `${day}/${month}/${year}`;
|
|
1694
|
+
}
|
|
1695
|
+
function parseDate(dateString) {
|
|
1696
|
+
const parts = dateString.split("/");
|
|
1697
|
+
if (parts.length !== 3) return null;
|
|
1698
|
+
const day = parseInt(parts[0], 10);
|
|
1699
|
+
const month = parseInt(parts[1], 10) - 1;
|
|
1700
|
+
const year = parseInt(parts[2], 10);
|
|
1701
|
+
if (isNaN(day) || isNaN(month) || isNaN(year)) return null;
|
|
1702
|
+
const date = new Date(year, month, day);
|
|
1703
|
+
if (date.getDate() !== day || date.getMonth() !== month || date.getFullYear() !== year) {
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
return date;
|
|
1707
|
+
}
|
|
1708
|
+
function isValidDate(date) {
|
|
1709
|
+
return date instanceof Date && !isNaN(date.getTime());
|
|
1710
|
+
}
|
|
1711
|
+
function startOfDay2(date) {
|
|
1712
|
+
const d = new Date(date);
|
|
1713
|
+
d.setHours(0, 0, 0, 0);
|
|
1714
|
+
return d;
|
|
1715
|
+
}
|
|
1716
|
+
var DateInput = React11.forwardRef(
|
|
1717
|
+
({
|
|
1718
|
+
label,
|
|
1719
|
+
value,
|
|
1720
|
+
onChange,
|
|
1721
|
+
error = false,
|
|
1722
|
+
disabled = false,
|
|
1723
|
+
minDate,
|
|
1724
|
+
maxDate,
|
|
1725
|
+
placeholder = "00/00/0000",
|
|
1726
|
+
className = "",
|
|
1727
|
+
...props
|
|
1728
|
+
}, ref) => {
|
|
1729
|
+
const [inputValue, setInputValue] = React11.useState(
|
|
1730
|
+
value && isValidDate(value) ? formatDate(value) : placeholder
|
|
1731
|
+
);
|
|
1732
|
+
const [isFocused, setIsFocused] = React11.useState(false);
|
|
1733
|
+
const [showCalendar, setShowCalendar] = React11.useState(false);
|
|
1734
|
+
const [initialDateSet, setInitialDateSet] = React11.useState(true);
|
|
1735
|
+
const containerRef = React11.useRef(null);
|
|
1736
|
+
React11.useEffect(() => {
|
|
1737
|
+
if (value && isValidDate(value)) {
|
|
1738
|
+
setInputValue(formatDate(value));
|
|
1739
|
+
} else {
|
|
1740
|
+
setInputValue(placeholder);
|
|
1741
|
+
}
|
|
1742
|
+
}, [value, placeholder]);
|
|
1743
|
+
React11.useEffect(() => {
|
|
1744
|
+
if (showCalendar && initialDateSet) {
|
|
1745
|
+
const today = /* @__PURE__ */ new Date();
|
|
1746
|
+
onChange(today);
|
|
1747
|
+
setInputValue(formatDate(today));
|
|
1748
|
+
setInitialDateSet(false);
|
|
1749
|
+
}
|
|
1750
|
+
}, [showCalendar, initialDateSet, onChange]);
|
|
1751
|
+
React11.useEffect(() => {
|
|
1752
|
+
const handleClickOutside = (event) => {
|
|
1753
|
+
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
|
1754
|
+
setShowCalendar(false);
|
|
1755
|
+
setInitialDateSet(false);
|
|
1756
|
+
}
|
|
1757
|
+
};
|
|
1758
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1759
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
1760
|
+
}, []);
|
|
1761
|
+
const handleInputChange = (e) => {
|
|
1762
|
+
let rawValue = e.target.value;
|
|
1763
|
+
rawValue = rawValue.replace(/\D/g, "").replace(/^(\d{2})/, "$1/").replace(/^(\d{2}\/\d{2})/, "$1/").slice(0, 10);
|
|
1764
|
+
setInputValue(rawValue);
|
|
1765
|
+
if (rawValue.length === 10) {
|
|
1766
|
+
const parsedDate = parseDate(rawValue);
|
|
1767
|
+
if (parsedDate && isValidDate(parsedDate)) {
|
|
1768
|
+
onChange(parsedDate);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
};
|
|
1772
|
+
const handleCalendarSelect = (date) => {
|
|
1773
|
+
const selectedDate = parseDate(inputValue);
|
|
1774
|
+
const sameDay = selectedDate && isValidDate(selectedDate) && selectedDate.getDate() === date.getDate() && selectedDate.getMonth() === date.getMonth() && selectedDate.getFullYear() === date.getFullYear();
|
|
1775
|
+
if (!sameDay) {
|
|
1776
|
+
onChange(date);
|
|
1777
|
+
setInputValue(formatDate(date));
|
|
1778
|
+
}
|
|
1779
|
+
setInitialDateSet(false);
|
|
1780
|
+
setShowCalendar(false);
|
|
1781
|
+
};
|
|
1782
|
+
const handleInputFocus = () => {
|
|
1783
|
+
setIsFocused(true);
|
|
1784
|
+
setShowCalendar(true);
|
|
1785
|
+
};
|
|
1786
|
+
const handleInputBlur = () => {
|
|
1787
|
+
setIsFocused(false);
|
|
1788
|
+
const parsed = parseDate(inputValue);
|
|
1789
|
+
if (!parsed || !isValidDate(parsed)) {
|
|
1790
|
+
const today = /* @__PURE__ */ new Date();
|
|
1791
|
+
onChange(today);
|
|
1792
|
+
setInputValue(formatDate(today));
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
const isEmpty = inputValue === placeholder;
|
|
1796
|
+
const textColorClass = isEmpty ? "text-[var(--muted-fg)]" : "";
|
|
1797
|
+
return /* @__PURE__ */ jsxs("div", { className: ["relative w-full", className].join(" "), ref: containerRef, children: [
|
|
1798
|
+
/* @__PURE__ */ jsx(
|
|
1799
|
+
Input,
|
|
1800
|
+
{
|
|
1801
|
+
ref,
|
|
1802
|
+
label,
|
|
1803
|
+
type: "text",
|
|
1804
|
+
value: inputValue,
|
|
1805
|
+
onChange: handleInputChange,
|
|
1806
|
+
onFocus: handleInputFocus,
|
|
1807
|
+
onBlur: handleInputBlur,
|
|
1808
|
+
onClick: () => !disabled && setShowCalendar(true),
|
|
1809
|
+
disabled,
|
|
1810
|
+
error,
|
|
1811
|
+
icon: "calendar_today",
|
|
1812
|
+
className: textColorClass,
|
|
1813
|
+
...props
|
|
1814
|
+
}
|
|
1815
|
+
),
|
|
1816
|
+
showCalendar && !disabled && /* @__PURE__ */ jsx("div", { className: "absolute z-20 mt-2 w-full max-w-sm", children: /* @__PURE__ */ jsx(Card, { className: "p-4", showDecorations: false, children: /* @__PURE__ */ jsx(
|
|
1817
|
+
Calendar,
|
|
1818
|
+
{
|
|
1819
|
+
selectedDate: inputValue !== placeholder && parseDate(inputValue) && isValidDate(parseDate(inputValue)) ? parseDate(inputValue) : /* @__PURE__ */ new Date(),
|
|
1820
|
+
onDateSelect: handleCalendarSelect,
|
|
1821
|
+
today: startOfDay2(/* @__PURE__ */ new Date()),
|
|
1822
|
+
minDate,
|
|
1823
|
+
maxDate
|
|
1824
|
+
}
|
|
1825
|
+
) }) })
|
|
1826
|
+
] });
|
|
1827
|
+
}
|
|
1828
|
+
);
|
|
1829
|
+
DateInput.displayName = "DateInput";
|
|
1531
1830
|
|
|
1532
|
-
export { AgentButton_default as AgentButton, AnimatedBgCircle_default as AnimatedBgCircle, AnimatedBgRectangle_default as AnimatedBgRectangle, AppBackground, Autocomplete, Avatar, AvatarGroup, BackgroundBlur, Breadcrumb, Button, Card, Chip, Counter, Icon, IconButton, Input, MessageBubble, Modal, Search, Separator, Skeleton, Textarea, Typo, assets_exports as assets };
|
|
1831
|
+
export { AgentButton_default as AgentButton, AnimatedBgCircle_default as AnimatedBgCircle, AnimatedBgRectangle_default as AnimatedBgRectangle, AppBackground, Autocomplete, Avatar, AvatarGroup, BackgroundBlur, Breadcrumb, Button, Calendar, Card, Chip, Counter, DateInput, 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.6.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": [
|
|
@@ -167,7 +167,7 @@ export default function Autocomplete({
|
|
|
167
167
|
>
|
|
168
168
|
<fieldset
|
|
169
169
|
className={[
|
|
170
|
-
"w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-
|
|
170
|
+
"w-full min-w-0 rounded-full border bg-[var(--surface)] transition-colors h-14",
|
|
171
171
|
"border-[var(--border)] focus-within:border-[var(--color-brand)]",
|
|
172
172
|
disabled ? "opacity-60 cursor-not-allowed" : ""
|
|
173
173
|
].join(" ")}
|