@eintrek/erp-theme 1.4.7 → 1.4.9

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.esm.js CHANGED
@@ -163,8 +163,13 @@ function AspectRatio({ ...props }) {
163
163
  function Avatar({ className, ...props }) {
164
164
  return (jsx(AvatarPrimitive.Root, { "data-slot": "avatar", className: cn$1("relative flex size-8 shrink-0 overflow-hidden rounded-full", className), ...props }));
165
165
  }
166
- function AvatarImage({ className, ...props }) {
167
- return (jsx(AvatarPrimitive.Image, { "data-slot": "avatar-image", className: cn$1("aspect-square size-full", className), ...props }));
166
+ function AvatarImage({ className, src, ...props }) {
167
+ // Empty/undefined src still mounts an <img> in some browsers and shows a
168
+ // broken-image glyph over AvatarFallback (e.g. a black "N" badge). Skip it.
169
+ if (src == null || src === "") {
170
+ return null;
171
+ }
172
+ return (jsx(AvatarPrimitive.Image, { "data-slot": "avatar-image", src: src, className: cn$1("aspect-square size-full", className), ...props }));
168
173
  }
169
174
  function AvatarFallback({ className, ...props }) {
170
175
  return (jsx(AvatarPrimitive.Fallback, { "data-slot": "avatar-fallback", className: cn$1("bg-muted flex size-full items-center justify-center rounded-full", className), ...props }));
@@ -13408,6 +13413,222 @@ function HoverCardContent({ className, align = "center", sideOffset = 4, ...prop
13408
13413
  return (jsx(HoverCardPrimitive.Portal, { "data-slot": "hover-card-portal", children: jsx(HoverCardPrimitive.Content, { "data-slot": "hover-card-content", align: align, sideOffset: sideOffset, className: cn$1("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden", className), ...props }) }));
13409
13414
  }
13410
13415
 
13416
+ function countDigitsBefore$1(str, caret) {
13417
+ let n = 0;
13418
+ for (let i = 0; i < caret && i < str.length; i++) {
13419
+ if (/\d/.test(str[i]))
13420
+ n++;
13421
+ }
13422
+ return n;
13423
+ }
13424
+ function caretAfterDigits$1(str, digitCount) {
13425
+ if (digitCount <= 0)
13426
+ return 0;
13427
+ let seen = 0;
13428
+ for (let i = 0; i < str.length; i++) {
13429
+ if (/\d/.test(str[i])) {
13430
+ seen++;
13431
+ if (seen >= digitCount)
13432
+ return i + 1;
13433
+ }
13434
+ }
13435
+ return str.length;
13436
+ }
13437
+ function stripToRaw(input, allowNegative) {
13438
+ let s = input.replace(/,/g, "");
13439
+ const neg = allowNegative && s.startsWith("-");
13440
+ s = s.replace(/[^\d.]/g, "");
13441
+ // Keep only the first decimal point.
13442
+ const dot = s.indexOf(".");
13443
+ if (dot !== -1) {
13444
+ s = s.slice(0, dot + 1) + s.slice(dot + 1).replace(/\./g, "");
13445
+ }
13446
+ return neg ? `-${s}` : s;
13447
+ }
13448
+ function formatRaw(raw, decimals) {
13449
+ if (raw === "" || raw === "-" || raw === ".")
13450
+ return raw === "." ? "0." : raw;
13451
+ const neg = raw.startsWith("-");
13452
+ const body = neg ? raw.slice(1) : raw;
13453
+ const [intPart = "", fracPart] = body.split(".");
13454
+ const intFormatted = intPart === ""
13455
+ ? ""
13456
+ : Number(intPart).toLocaleString("en-US", { maximumFractionDigits: 0 });
13457
+ let out = neg ? `-${intFormatted}` : intFormatted;
13458
+ if (fracPart !== undefined) {
13459
+ out += `.${fracPart.slice(0, decimals)}`;
13460
+ }
13461
+ return out;
13462
+ }
13463
+ function parseRaw(raw) {
13464
+ if (raw === "" || raw === "-" || raw === "." || raw === "-.")
13465
+ return "";
13466
+ const n = Number(raw);
13467
+ return Number.isFinite(n) ? n : "";
13468
+ }
13469
+ /**
13470
+ * Number input that shows thousand separators while typing (e.g. 100,000).
13471
+ * Emits a numeric model value without commas.
13472
+ */
13473
+ function FormattedNumberInput({ value, onChange, decimals = 2, allowNegative = false, className, onBlur, ...props }) {
13474
+ const inputRef = React.useRef(null);
13475
+ const numeric = typeof value === "number" && Number.isFinite(value) ? value : undefined;
13476
+ const toRawFromNumeric = (n) => {
13477
+ if (decimals <= 0)
13478
+ return String(Math.trunc(n));
13479
+ // Avoid scientific notation; trim trailing zeros for cleaner edits.
13480
+ const fixed = n.toFixed(decimals);
13481
+ return fixed.replace(/\.?0+$/, "") || "0";
13482
+ };
13483
+ const [raw, setRaw] = React.useState(() => numeric === undefined ? "" : toRawFromNumeric(numeric));
13484
+ // Sync from external value when not actively mismatched (controlled reset).
13485
+ React.useEffect(() => {
13486
+ if (numeric === undefined) {
13487
+ if (raw !== "" && raw !== "-" && parseRaw(raw) === "") {
13488
+ // keep incomplete draft
13489
+ return;
13490
+ }
13491
+ if (parseRaw(raw) === "")
13492
+ setRaw("");
13493
+ return;
13494
+ }
13495
+ if (parseRaw(raw) === numeric)
13496
+ return;
13497
+ setRaw(toRawFromNumeric(numeric));
13498
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- only react to external value
13499
+ }, [numeric]);
13500
+ const display = formatRaw(raw, decimals);
13501
+ const accepts = React.useMemo(() => {
13502
+ const sign = allowNegative ? "-?" : "";
13503
+ return new RegExp(`^${sign}\\d*\\.?\\d{0,${decimals}}$`);
13504
+ }, [allowNegative, decimals]);
13505
+ return (jsx(Input, { ...props, ref: inputRef, type: "text", inputMode: "decimal", autoComplete: "off", className: cn$1(className), value: display, onChange: (event) => {
13506
+ const el = event.target;
13507
+ const nextRaw = stripToRaw(el.value, allowNegative);
13508
+ if (!accepts.test(nextRaw) && nextRaw !== "" && nextRaw !== "-") {
13509
+ return;
13510
+ }
13511
+ const digitsBefore = countDigitsBefore$1(el.value, el.selectionStart ?? el.value.length);
13512
+ setRaw(nextRaw);
13513
+ onChange(parseRaw(nextRaw));
13514
+ const nextDisplay = formatRaw(nextRaw, decimals);
13515
+ requestAnimationFrame(() => {
13516
+ const node = inputRef.current;
13517
+ if (!node)
13518
+ return;
13519
+ const pos = caretAfterDigits$1(nextDisplay, digitsBefore);
13520
+ node.setSelectionRange(pos, pos);
13521
+ });
13522
+ }, onBlur: (event) => {
13523
+ const parsed = parseRaw(raw);
13524
+ if (parsed === "") {
13525
+ setRaw("");
13526
+ onChange("");
13527
+ }
13528
+ else {
13529
+ const normalized = toRawFromNumeric(parsed);
13530
+ setRaw(normalized);
13531
+ onChange(parsed);
13532
+ }
13533
+ onBlur?.(event);
13534
+ } }));
13535
+ }
13536
+
13537
+ /** `#` = digit slot; any other character is a literal shown while typing. */
13538
+ const MASK_PRESETS = {
13539
+ /** Thai bank-style account: 555-5-55555-5 */
13540
+ bankAccount: "###-#-#####-#",
13541
+ /** Thai mobile (10 digits): 081-234-5678 */
13542
+ thaiPhone: "###-###-####",
13543
+ /** Thai national ID (13 digits): 1-2345-67890-12-3 */
13544
+ thaiNationalId: "#-####-#####-##-#",
13545
+ };
13546
+ function resolveMask(mask) {
13547
+ if (mask in MASK_PRESETS) {
13548
+ return MASK_PRESETS[mask];
13549
+ }
13550
+ return mask;
13551
+ }
13552
+ function maxDigits(pattern) {
13553
+ let n = 0;
13554
+ for (const ch of pattern)
13555
+ if (ch === "#")
13556
+ n++;
13557
+ return n;
13558
+ }
13559
+ /**
13560
+ * Map digits onto `#` slots. Literals appear as soon as the previous `#` is
13561
+ * filled; after the last typed digit, the next separator is shown so the mask
13562
+ * is visible while typing (e.g. "555-").
13563
+ */
13564
+ function formatMasked(digits, pattern) {
13565
+ if (!digits)
13566
+ return "";
13567
+ let di = 0;
13568
+ let out = "";
13569
+ for (let i = 0; i < pattern.length; i++) {
13570
+ const ch = pattern[i];
13571
+ if (ch === "#") {
13572
+ if (di >= digits.length)
13573
+ break;
13574
+ out += digits[di++];
13575
+ }
13576
+ else {
13577
+ if (di === 0)
13578
+ break;
13579
+ out += ch;
13580
+ }
13581
+ }
13582
+ return out;
13583
+ }
13584
+ function parseDigits(input, pattern) {
13585
+ return input.replace(/\D/g, "").slice(0, maxDigits(pattern));
13586
+ }
13587
+ function countDigitsBefore(str, caret) {
13588
+ let n = 0;
13589
+ for (let i = 0; i < caret && i < str.length; i++) {
13590
+ if (/\d/.test(str[i]))
13591
+ n++;
13592
+ }
13593
+ return n;
13594
+ }
13595
+ function caretAfterDigits(str, digitCount) {
13596
+ if (digitCount <= 0)
13597
+ return 0;
13598
+ let seen = 0;
13599
+ for (let i = 0; i < str.length; i++) {
13600
+ if (/\d/.test(str[i])) {
13601
+ seen++;
13602
+ if (seen >= digitCount)
13603
+ return i + 1;
13604
+ }
13605
+ }
13606
+ return str.length;
13607
+ }
13608
+ /**
13609
+ * Masked text input that shows separators while typing (e.g. 555-5-55555-5).
13610
+ * `value` / `onChange` use digits only.
13611
+ */
13612
+ function MaskedTextInput({ value, onChange, mask, className, ...props }) {
13613
+ const inputRef = React.useRef(null);
13614
+ const pattern = resolveMask(mask);
13615
+ const display = formatMasked(value ?? "", pattern);
13616
+ return (jsx(Input, { ...props, ref: inputRef, type: "text", inputMode: "numeric", autoComplete: "off", className: cn$1(className), value: display, onChange: (event) => {
13617
+ const el = event.target;
13618
+ const digitsBefore = countDigitsBefore(el.value, el.selectionStart ?? el.value.length);
13619
+ const next = parseDigits(el.value, pattern);
13620
+ onChange(next);
13621
+ const nextDisplay = formatMasked(next, pattern);
13622
+ requestAnimationFrame(() => {
13623
+ const node = inputRef.current;
13624
+ if (!node)
13625
+ return;
13626
+ const pos = caretAfterDigits(nextDisplay, digitsBefore);
13627
+ node.setSelectionRange(pos, pos);
13628
+ });
13629
+ } }));
13630
+ }
13631
+
13411
13632
  function InputField({ id, label, helperText, error, required, containerClassName, labelClassName, messageClassName, className, ref, ...props }) {
13412
13633
  const reactId = React.useId();
13413
13634
  const inputId = id ?? reactId;
@@ -34577,5 +34798,5 @@ const flagConfig = {
34577
34798
  ],
34578
34799
  };
34579
34800
 
34580
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandBlock, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CustomSelectField2, DataTable, DataTableCell, DataTableColumnHeader, DataTableDateFilter, DataTableFacetedFilter, DataTableFilterDropdown, DataTableFilterList, DataTablePagination, DataTableRangeFilter, DataTableSkeleton, DataTableSliderFilter, DataTableSortList, DataTableToolbar, DataTableViewOptions, DateInput, DatePicker, DatePickerField, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Faceted, FacetedBadgeList, FacetedContent, FacetedEmpty, FacetedGroup, FacetedInput, FacetedItem, FacetedList, FacetedSeparator, FacetedTrigger, FileUploader, Footer, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormSkeleton, Header, HoverCard, HoverCardContent, HoverCardTrigger, Input, InputField, InputOTP, InputOTPGroup, InputOTPSlot, Label, LoadingScreen, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, PageHeader, Pagination$1 as Pagination, PaginationContent$1 as PaginationContent, PaginationEllipsis$1 as PaginationEllipsis, PaginationItem$1 as PaginationItem, PaginationLink$1 as PaginationLink, PaginationNext$1 as PaginationNext, PaginationPrevious$1 as PaginationPrevious, Popover, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, RegistryItemRow, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, ScrollBar, Select, SelectContent, SelectField, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarTrigger, Skeleton, Slider, Sortable, SortableContent, SortableItem, SortableItemHandle, SortableOverlay, Switch, THAI_MONTH_NAMES, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, TextareaField, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, ValidationErrors, badgeVariants, buttonVariants, cn$1 as cn, convertFileToUploadedFile, convertFilesToUploadedFiles, dataTableConfig, databasePrefix, flagConfig, formatCurrency, formatDate, formatDateThai, formatDateThaiShort, formatFileSize, formatNumber, formatThaiMonth, formatThaiMonthShort, generateFileId, generateId, getBaseUrl, getCommonPinningStyles, getDefaultFilterOperator, getFilterOperators, getFiltersStateParser, getMonthDateRange, getSortingStateParser, getThaiMonthName, getValidFilters, isImageFile, navigationMenuTriggerStyle, siteConfig, thaiBahtText, toBuddhistIso, toBuddhistYear, toGregorianYear, toggleVariants, unknownError, useCallbackRef, useDataTable, useDebounce, useDebouncedCallback, useFormField, useIsMobile, useMediaQuery, validateFileSize, validateFileType };
34801
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandBlock, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CustomSelectField2, DataTable, DataTableCell, DataTableColumnHeader, DataTableDateFilter, DataTableFacetedFilter, DataTableFilterDropdown, DataTableFilterList, DataTablePagination, DataTableRangeFilter, DataTableSkeleton, DataTableSliderFilter, DataTableSortList, DataTableToolbar, DataTableViewOptions, DateInput, DatePicker, DatePickerField, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Faceted, FacetedBadgeList, FacetedContent, FacetedEmpty, FacetedGroup, FacetedInput, FacetedItem, FacetedList, FacetedSeparator, FacetedTrigger, FileUploader, Footer, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormSkeleton, FormattedNumberInput, Header, HoverCard, HoverCardContent, HoverCardTrigger, Input, InputField, InputOTP, InputOTPGroup, InputOTPSlot, Label, LoadingScreen, MASK_PRESETS, MaskedTextInput, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, PageHeader, Pagination$1 as Pagination, PaginationContent$1 as PaginationContent, PaginationEllipsis$1 as PaginationEllipsis, PaginationItem$1 as PaginationItem, PaginationLink$1 as PaginationLink, PaginationNext$1 as PaginationNext, PaginationPrevious$1 as PaginationPrevious, Popover, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, RegistryItemRow, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, ScrollBar, Select, SelectContent, SelectField, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarTrigger, Skeleton, Slider, Sortable, SortableContent, SortableItem, SortableItemHandle, SortableOverlay, Switch, THAI_MONTH_NAMES, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, TextareaField, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, ValidationErrors, badgeVariants, buttonVariants, cn$1 as cn, convertFileToUploadedFile, convertFilesToUploadedFiles, dataTableConfig, databasePrefix, flagConfig, formatCurrency, formatDate, formatDateThai, formatDateThaiShort, formatFileSize, formatMasked as formatMaskedValue, formatNumber, formatThaiMonth, formatThaiMonthShort, generateFileId, generateId, getBaseUrl, getCommonPinningStyles, getDefaultFilterOperator, getFilterOperators, getFiltersStateParser, getMonthDateRange, getSortingStateParser, getThaiMonthName, getValidFilters, isImageFile, navigationMenuTriggerStyle, parseDigits as parseMaskedDigits, siteConfig, thaiBahtText, toBuddhistIso, toBuddhistYear, toGregorianYear, toggleVariants, unknownError, useCallbackRef, useDataTable, useDebounce, useDebouncedCallback, useFormField, useIsMobile, useMediaQuery, validateFileSize, validateFileType };
34581
34802
  //# sourceMappingURL=index.esm.js.map