@bsol-oss/react-datatable5 12.0.0-beta.90 → 12.0.0-beta.92

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.
Files changed (31) hide show
  1. package/dist/index.d.ts +38 -7
  2. package/dist/index.js +1242 -521
  3. package/dist/index.mjs +1244 -525
  4. package/dist/types/components/DataTable/display/RecordDisplay.d.ts +2 -2
  5. package/dist/types/components/DataTable/display/TextCell.d.ts +11 -4
  6. package/dist/types/components/DataTable/display/TextWithCopy.d.ts +8 -0
  7. package/dist/types/components/DatePicker/DatePickerInput.d.ts +18 -0
  8. package/dist/types/components/DatePicker/DateTimePicker.d.ts +4 -1
  9. package/dist/types/components/DatePicker/IsoTimePicker.d.ts +2 -1
  10. package/dist/types/components/DatePicker/index.d.ts +2 -1
  11. package/dist/types/components/Form/SchemaFormContext.d.ts +2 -1
  12. package/dist/types/components/Form/components/core/FormRoot.d.ts +3 -4
  13. package/dist/types/components/Form/components/fields/ArrayRenderer.d.ts +1 -1
  14. package/dist/types/components/Form/components/fields/StringInputField.d.ts +0 -1
  15. package/dist/types/components/Form/components/fields/TextAreaInput.d.ts +0 -5
  16. package/dist/types/components/Form/components/types/CustomJSONSchema7.d.ts +8 -0
  17. package/dist/types/components/Form/components/viewers/ArrayViewer.d.ts +1 -1
  18. package/dist/types/components/Form/components/viewers/BooleanViewer.d.ts +1 -1
  19. package/dist/types/components/Form/components/viewers/DateTimeViewer.d.ts +1 -1
  20. package/dist/types/components/Form/components/viewers/DateViewer.d.ts +1 -1
  21. package/dist/types/components/Form/components/viewers/IdViewer.d.ts +1 -1
  22. package/dist/types/components/Form/components/viewers/ObjectViewer.d.ts +1 -1
  23. package/dist/types/components/Form/components/viewers/RecordViewer.d.ts +2 -2
  24. package/dist/types/components/Form/components/viewers/StringViewer.d.ts +1 -6
  25. package/dist/types/components/Form/components/viewers/TextAreaViewer.d.ts +1 -6
  26. package/dist/types/components/Form/components/viewers/TimeViewer.d.ts +1 -1
  27. package/dist/types/components/Form/utils/useFormI18n.d.ts +5 -3
  28. package/dist/types/components/TimePicker/TimePicker.d.ts +2 -1
  29. package/dist/types/components/ui/pagination.d.ts +10 -7
  30. package/dist/types/index.d.ts +1 -0
  31. package/package.json +4 -4
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, CheckboxCard as CheckboxCard$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, Tooltip as Tooltip$1, Group, InputElement, Icon, EmptyState as EmptyState$2, VStack, List, Table as Table$1, Checkbox as Checkbox$1, Card, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Tag as Tag$1, Image, Alert, Field as Field$1, Popover, useFilter, useListCollection, Combobox, Tabs, Skeleton, NumberInput, Show, RadioCard, CheckboxGroup, InputGroup as InputGroup$1, Center, Heading } from '@chakra-ui/react';
2
+ import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, CheckboxCard as CheckboxCard$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, Tooltip as Tooltip$1, Group, InputElement, Icon, EmptyState as EmptyState$2, VStack, List, Table as Table$1, Checkbox as Checkbox$1, Card, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Clipboard, Badge, Link, Tag as Tag$1, Image, Alert, Field as Field$1, Popover, useFilter, useListCollection, Combobox, Tabs, Skeleton, NumberInput, Show, RadioCard, CheckboxGroup, InputGroup as InputGroup$1, Center, Heading } from '@chakra-ui/react';
3
3
  import { AiOutlineColumnWidth } from 'react-icons/ai';
4
4
  import * as React from 'react';
5
- import React__default, { createContext, useContext, useState, useEffect, useRef, useMemo, forwardRef } from 'react';
6
- import { LuX, LuCheck, LuChevronRight, LuSearch, LuImage, LuFile } from 'react-icons/lu';
5
+ import React__default, { createContext, useContext, useState, useEffect, useRef, useMemo, forwardRef, useCallback } from 'react';
6
+ import { LuX, LuCheck, LuChevronRight, LuCopy, LuExternalLink, LuSearch, LuImage, LuFile } from 'react-icons/lu';
7
7
  import { MdOutlineSort, MdFilterAlt, MdSearch, MdOutlineChecklist, MdClear, MdOutlineViewColumn, MdFilterListAlt, MdPushPin, MdCancel, MdDateRange } from 'react-icons/md';
8
8
  import { FaUpDown, FaGripLinesVertical, FaTrash } from 'react-icons/fa6';
9
9
  import { BiDownArrow, BiUpArrow, BiError } from 'react-icons/bi';
@@ -33,6 +33,7 @@ import dayjs from 'dayjs';
33
33
  import utc from 'dayjs/plugin/utc';
34
34
  import timezone from 'dayjs/plugin/timezone';
35
35
  import { TiDeleteOutline } from 'react-icons/ti';
36
+ import customParseFormat from 'dayjs/plugin/customParseFormat';
36
37
  import { rankItem } from '@tanstack/match-sorter-utils';
37
38
 
38
39
  const DataTableContext = createContext({
@@ -541,16 +542,111 @@ const { withContext } = createRecipeContext({ key: "button" });
541
542
  const LinkButton = withContext("a");
542
543
 
543
544
  const [RootPropsProvider, useRootProps] = createContext$1({
544
- name: "RootPropsProvider",
545
+ name: 'RootPropsProvider',
545
546
  });
546
547
  const variantMap = {
547
- outline: { default: "ghost", ellipsis: "plain", current: "outline" },
548
- solid: { default: "outline", ellipsis: "outline", current: "solid" },
549
- subtle: { default: "ghost", ellipsis: "plain", current: "subtle" },
548
+ outline: { default: 'ghost', ellipsis: 'plain', current: 'outline' },
549
+ solid: { default: 'outline', ellipsis: 'outline', current: 'solid' },
550
+ subtle: { default: 'ghost', ellipsis: 'plain', current: 'subtle' },
550
551
  };
551
552
  const PaginationRoot = React.forwardRef(function PaginationRoot(props, ref) {
552
- const { size = "sm", variant = "outline", getHref, ...rest } = props;
553
- return (jsx(RootPropsProvider, { value: { size, variantMap: variantMap[variant], getHref }, children: jsx(Pagination$1.Root, { ref: ref, type: getHref ? "link" : "button", ...rest }) }));
553
+ const { size = 'sm', variant = 'outline', getHref, siblingCount, minSiblingCount = 1, maxSiblingCount, ...rest } = props;
554
+ const containerRef = React.useRef(null);
555
+ const [calculatedSiblingCount, setCalculatedSiblingCount] = React.useState(siblingCount);
556
+ React.useEffect(() => {
557
+ if (siblingCount !== undefined || !containerRef.current) {
558
+ setCalculatedSiblingCount(siblingCount);
559
+ return;
560
+ }
561
+ const container = containerRef.current;
562
+ let rafId = null;
563
+ const calculateSiblingCount = () => {
564
+ if (!container)
565
+ return;
566
+ const width = container.offsetWidth;
567
+ if (width === 0)
568
+ return;
569
+ // Estimate button width based on size
570
+ // These are approximate widths including padding for different button sizes
571
+ const buttonWidthMap = {
572
+ xs: 28,
573
+ sm: 36,
574
+ md: 40,
575
+ lg: 44,
576
+ };
577
+ let buttonWidth = buttonWidthMap[size] || 36;
578
+ // Try to measure actual button if available (for more accuracy)
579
+ const buttons = container.querySelectorAll('button');
580
+ if (buttons.length > 0) {
581
+ const firstButton = buttons[0];
582
+ if (firstButton.offsetWidth > 0) {
583
+ // Use measured width, but account for text content variation
584
+ const measuredWidth = firstButton.offsetWidth;
585
+ // Page number buttons might be slightly wider due to text, use measured width
586
+ buttonWidth = Math.max(buttonWidth, measuredWidth);
587
+ }
588
+ }
589
+ // Account for prev/next buttons and gaps
590
+ // HStack gap is typically 8px in Chakra UI
591
+ const gap = 8;
592
+ const prevNextWidth = buttonWidth * 2 + gap;
593
+ const availableWidth = Math.max(0, width - prevNextWidth);
594
+ // Each page button takes buttonWidth + gap
595
+ const buttonWithGap = buttonWidth + gap;
596
+ const maxButtons = Math.floor(availableWidth / buttonWithGap);
597
+ // Calculate sibling count
598
+ // Minimum structure: [prev] [1] [current] [last] [next] = 5 buttons
599
+ // With siblings: [prev] [1] [...] [current-N] ... [current] ... [current+N] [...] [last] [next]
600
+ // We need: prev(1) + first(1) + ellipsis(1) + siblings*2 + current(1) + ellipsis(1) + last(1) + next(1)
601
+ // Minimum: 5 buttons (prev, first, current, last, next)
602
+ // With siblings: 5 + siblings*2 + ellipsis*2 (if needed)
603
+ const minRequired = 5;
604
+ const extraButtons = Math.max(0, maxButtons - minRequired);
605
+ // Calculate sibling count
606
+ // If we have enough space for ellipsis (2 buttons), account for that
607
+ let calculated = minSiblingCount;
608
+ if (extraButtons >= 4) {
609
+ // Space for ellipsis (2) + siblings
610
+ calculated = Math.floor((extraButtons - 2) / 2);
611
+ }
612
+ else if (extraButtons >= 2) {
613
+ // Space for some siblings but not ellipsis
614
+ calculated = Math.floor(extraButtons / 2);
615
+ }
616
+ // Apply max limit if provided
617
+ if (maxSiblingCount !== undefined) {
618
+ calculated = Math.min(calculated, maxSiblingCount);
619
+ }
620
+ setCalculatedSiblingCount(Math.max(minSiblingCount, calculated));
621
+ };
622
+ const resizeObserver = new ResizeObserver(() => {
623
+ // Use requestAnimationFrame to debounce and ensure DOM is updated
624
+ if (rafId !== null) {
625
+ cancelAnimationFrame(rafId);
626
+ }
627
+ rafId = requestAnimationFrame(calculateSiblingCount);
628
+ });
629
+ resizeObserver.observe(container);
630
+ // Initial calculation after a short delay to ensure buttons are rendered
631
+ const timeoutId = setTimeout(calculateSiblingCount, 100);
632
+ return () => {
633
+ resizeObserver.disconnect();
634
+ if (rafId !== null) {
635
+ cancelAnimationFrame(rafId);
636
+ }
637
+ clearTimeout(timeoutId);
638
+ };
639
+ }, [size, siblingCount, minSiblingCount, maxSiblingCount]);
640
+ const mergedRef = React.useCallback((node) => {
641
+ if (typeof ref === 'function') {
642
+ ref(node);
643
+ }
644
+ else if (ref) {
645
+ ref.current = node;
646
+ }
647
+ containerRef.current = node;
648
+ }, [ref]);
649
+ return (jsx(RootPropsProvider, { value: { size, variantMap: variantMap[variant], getHref }, children: jsx(Pagination$1.Root, { ref: mergedRef, type: getHref ? 'link' : 'button', siblingCount: calculatedSiblingCount, ...rest }) }));
554
650
  });
555
651
  const PaginationEllipsis = React.forwardRef(function PaginationEllipsis(props, ref) {
556
652
  const { size, variantMap } = useRootProps();
@@ -584,16 +680,16 @@ const PaginationNextTrigger = React.forwardRef(function PaginationNextTrigger(pr
584
680
  });
585
681
  const PaginationItems = (props) => {
586
682
  return (jsx(Pagination$1.Context, { children: ({ pages }) => pages.map((page, index) => {
587
- return page.type === "ellipsis" ? (jsx(PaginationEllipsis, { index: index, ...props }, index)) : (jsx(PaginationItem, { type: "page", value: page.value, ...props }, index));
683
+ return page.type === 'ellipsis' ? (jsx(PaginationEllipsis, { index: index, ...props }, index)) : (jsx(PaginationItem, { type: "page", value: page.value, ...props }, index));
588
684
  }) }));
589
685
  };
590
686
  React.forwardRef(function PaginationPageText(props, ref) {
591
- const { format = "compact", ...rest } = props;
687
+ const { format = 'compact', ...rest } = props;
592
688
  const { page, totalPages, pageRange, count } = usePaginationContext();
593
689
  const content = React.useMemo(() => {
594
- if (format === "short")
690
+ if (format === 'short')
595
691
  return `${page} / ${totalPages}`;
596
- if (format === "compact")
692
+ if (format === 'compact')
597
693
  return `${page} / ${totalPages}`;
598
694
  return `${pageRange.start + 1} - ${Math.min(pageRange.end, count)} / ${count}`;
599
695
  }, [format, page, totalPages, pageRange, count]);
@@ -3081,14 +3177,14 @@ const DefaultCardTitle = () => {
3081
3177
  const TableCards = ({ isSelectable = false, showDisplayNameOnly = false, renderTitle = DefaultCardTitle, cardBodyProps = {}, }) => {
3082
3178
  const { table, rowSelection, setRowSelection } = useDataTableContext();
3083
3179
  return (jsx(Fragment, { children: table.getRowModel().rows.map((row) => {
3084
- return (jsx(Card.Root, { flex: '1 0 20rem', children: jsxs(Card.Body, { display: 'flex', flexFlow: 'column', gap: '0.5rem', ...cardBodyProps, children: [isSelectable && (jsx(Checkbox, { isChecked: isRowSelected(row.id, rowSelection),
3180
+ return (jsx(Card.Root, { flex: '1 0 20rem', children: jsxs(Card.Body, { display: 'flex', flexFlow: 'column', gap: '0.5rem', ...cardBodyProps, children: [isSelectable && (jsx(Checkbox, { checked: isRowSelected(row.id, rowSelection),
3085
3181
  disabled: !canRowSelect(row),
3086
3182
  // indeterminate: row.getIsSomeSelected(),
3087
3183
  onChange: createRowToggleHandler(row, rowSelection, setRowSelection) })), renderTitle(row), jsx(Grid, { templateColumns: 'auto 1fr', gap: '1rem', children: row.getVisibleCells().map((cell) => {
3088
- return (jsxs(Fragment, { children: [jsxs(Box, { children: [showDisplayNameOnly && (jsx(Text, { fontWeight: 'bold', children: cell.column.columnDef.meta?.displayName ??
3184
+ return (jsxs(Box, { display: "contents", children: [jsxs(Box, { children: [showDisplayNameOnly && (jsx(Text, { fontWeight: 'bold', children: cell.column.columnDef.meta?.displayName ??
3089
3185
  cell.column.id })), !showDisplayNameOnly && (jsx(Fragment, { children: flexRender(cell.column.columnDef.header,
3090
3186
  // @ts-expect-error Assuming the CellContext interface is the same as HeaderContext
3091
- cell.getContext()) }))] }, `chakra-table-cardcolumnid-${row.id}`), jsx(Box, { justifySelf: 'end', children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, `chakra-table-cardcolumn-${row.id}`)] }));
3187
+ cell.getContext()) }))] }), jsx(Box, { justifySelf: 'end', children: flexRender(cell.column.columnDef.cell, cell.getContext()) })] }, `chakra-table-cardcell-${cell.id}`));
3092
3188
  }) })] }) }, `chakra-table-card-${row.id}`));
3093
3189
  }) }));
3094
3190
  };
@@ -3119,9 +3215,13 @@ const TableFooter = ({ showSelector = false, alwaysShowSelector = true, }) => {
3119
3215
  }
3120
3216
  return false;
3121
3217
  };
3122
- return (jsx(Table$1.Footer, { children: table.getFooterGroups().map((footerGroup) => (jsxs(Table$1.Row, { display: 'flex', children: [showSelector && (jsxs(Table$1.Header, { padding: `${table.getDensityValue()}px`, onMouseEnter: () => handleRowHover(true), onMouseLeave: () => handleRowHover(false), display: 'grid', children: [isCheckBoxVisible() && (jsx(Box, { margin: '0rem', display: 'grid', justifyItems: 'center', alignItems: 'center', children: jsx(Checkbox, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px`, isChecked: areAllRowsSelected(table, rowSelection),
3123
- // indeterminate: areSomeRowsSelected(table, rowSelection),
3124
- onChange: createToggleAllRowsHandler(table, rowSelection, setRowSelection) }) })), !isCheckBoxVisible() && (jsx(Box, { as: "span", margin: '0rem', display: 'grid', justifyItems: 'center', alignItems: 'center', width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px` }))] })), footerGroup.headers.map((header) => (jsx(Table$1.Cell, { padding: '0', columnSpan: `${header.colSpan}`,
3218
+ return (jsx(Table$1.Footer, { children: table.getFooterGroups().map((footerGroup) => (jsxs(Table$1.Row, { display: 'flex', children: [showSelector && (jsx(Table$1.Cell, { padding: `${table.getDensityValue()}px`, onMouseEnter: () => handleRowHover(true), onMouseLeave: () => handleRowHover(false), display: 'grid', justifyItems: 'center', alignItems: 'center', color: {
3219
+ base: 'colorPalette.900',
3220
+ _dark: 'colorPalette.100',
3221
+ },
3222
+ bg: { base: 'colorPalette.50', _dark: 'colorPalette.950' }, children: isCheckBoxVisible() ? (jsx(Checkbox, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px`, checked: areAllRowsSelected(table, rowSelection),
3223
+ // indeterminate: areSomeRowsSelected(table, rowSelection),
3224
+ onChange: createToggleAllRowsHandler(table, rowSelection, setRowSelection) })) : (jsx(Box, { as: "span", width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px` })) })), footerGroup.headers.map((header) => (jsx(Table$1.Cell, { padding: '0', columnSpan: `${header.colSpan}`,
3125
3225
  // styling resize and pinning start
3126
3226
  maxWidth: `${header.getSize()}px`, width: `${header.getSize()}px`, display: 'grid', children: jsx(MenuRoot$1, { children: jsx(MenuTrigger$1, { asChild: true, children: jsx(Box, { padding: `${table.getDensityValue()}px`, display: 'flex', alignItems: 'center', justifyContent: 'start', borderRadius: '0rem', children: jsxs(Flex, { gap: "0.5rem", alignItems: 'center', children: [header.isPlaceholder
3127
3227
  ? null
@@ -3315,11 +3415,136 @@ const TableLoadingComponent = ({ render, }) => {
3315
3415
  return jsx(Fragment, { children: render(query.isLoading) });
3316
3416
  };
3317
3417
 
3318
- const TextCell = ({ label, containerProps = {}, textProps = {}, children, }) => {
3319
- if (label) {
3320
- return (jsx(Flex, { alignItems: "center", height: "100%", ...containerProps, children: jsx(Tooltip, { content: jsx(Text, { as: "span", overflow: "hidden", textOverflow: "ellipsis", children: label }), children: jsx(Text, { as: "span", overflow: "hidden", textOverflow: "ellipsis", wordBreak: "break-all", ...textProps, children: children }) }) }));
3418
+ // Helper function to highlight matching text
3419
+ const highlightText$1 = (text, searchTerm) => {
3420
+ if (!searchTerm || searchTerm.trim() === '') {
3421
+ return String(text);
3422
+ }
3423
+ const textStr = String(text);
3424
+ const searchLower = searchTerm.toLowerCase();
3425
+ const textLower = textStr.toLowerCase();
3426
+ const parts = [];
3427
+ let lastIndex = 0;
3428
+ let index = textLower.indexOf(searchLower, lastIndex);
3429
+ while (index !== -1) {
3430
+ // Add text before match
3431
+ if (index > lastIndex) {
3432
+ parts.push(textStr.substring(lastIndex, index));
3433
+ }
3434
+ // Add highlighted match
3435
+ parts.push(jsx(Text, { as: "mark", bg: {
3436
+ base: 'yellow.200',
3437
+ _dark: 'yellow.800',
3438
+ }, color: {
3439
+ base: 'gray.900',
3440
+ _dark: 'gray.100',
3441
+ }, px: 0.5, borderRadius: "sm", children: textStr.substring(index, index + searchTerm.length) }, index));
3442
+ lastIndex = index + searchTerm.length;
3443
+ index = textLower.indexOf(searchLower, lastIndex);
3444
+ }
3445
+ // Add remaining text
3446
+ if (lastIndex < textStr.length) {
3447
+ parts.push(textStr.substring(lastIndex));
3448
+ }
3449
+ return parts.length > 0 ? jsx(Fragment, { children: parts }) : textStr;
3450
+ };
3451
+ const TextWithCopy = ({ text, globalFilter, highlightedText, }) => {
3452
+ const textValue = String(text ?? '');
3453
+ const displayText = highlightedText !== undefined
3454
+ ? highlightedText
3455
+ : highlightText$1(textValue, globalFilter);
3456
+ return (jsxs(HStack, { gap: 2, alignItems: "center", children: [jsx(Text, { as: "span", children: displayText }), jsx(Clipboard.Root, { value: textValue, children: jsx(Clipboard.Trigger, { asChild: true, children: jsx(IconButton, { size: "xs", variant: "ghost", "aria-label": "Copy", fontSize: "1em", children: jsx(Clipboard.Indicator, { copied: jsx(LuCheck, {}), children: jsx(LuCopy, {}) }) }) }) })] }));
3457
+ };
3458
+
3459
+ // Helper function to highlight matching text
3460
+ const highlightText = (text, searchTerm) => {
3461
+ if (!searchTerm || searchTerm.trim() === '') {
3462
+ return String(text);
3463
+ }
3464
+ const textStr = String(text);
3465
+ const searchLower = searchTerm.toLowerCase();
3466
+ const textLower = textStr.toLowerCase();
3467
+ const parts = [];
3468
+ let lastIndex = 0;
3469
+ let index = textLower.indexOf(searchLower, lastIndex);
3470
+ while (index !== -1) {
3471
+ // Add text before match
3472
+ if (index > lastIndex) {
3473
+ parts.push(textStr.substring(lastIndex, index));
3474
+ }
3475
+ // Add highlighted match
3476
+ parts.push(jsx(Text, { as: "mark", bg: {
3477
+ base: 'yellow.200',
3478
+ _dark: 'yellow.800',
3479
+ }, color: {
3480
+ base: 'gray.900',
3481
+ _dark: 'gray.100',
3482
+ }, px: 0.5, borderRadius: "sm", children: textStr.substring(index, index + searchTerm.length) }, index));
3483
+ lastIndex = index + searchTerm.length;
3484
+ index = textLower.indexOf(searchLower, lastIndex);
3485
+ }
3486
+ // Add remaining text
3487
+ if (lastIndex < textStr.length) {
3488
+ parts.push(textStr.substring(lastIndex));
3489
+ }
3490
+ return parts.length > 0 ? jsx(Fragment, { children: parts }) : textStr;
3491
+ };
3492
+ const RenderValue = ({ text, href, onClick, isCopyable, isBadge, badgeColor, colorPalette, globalFilter, }) => {
3493
+ const highlightedText = useMemo(() => highlightText(text ?? '', globalFilter), [text, globalFilter]);
3494
+ if (isBadge) {
3495
+ return (jsx(Badge, { colorPalette: colorPalette || badgeColor, children: highlightedText }));
3496
+ }
3497
+ // onClick takes precedence over href
3498
+ if (onClick) {
3499
+ return (jsx(Box, { as: "button", onClick: onClick, cursor: "pointer", textAlign: "left", _hover: {
3500
+ textDecoration: 'underline',
3501
+ color: {
3502
+ base: 'blue.500',
3503
+ _dark: 'blue.400',
3504
+ },
3505
+ }, transition: "all 0.2s", children: highlightedText }));
3321
3506
  }
3322
- return (jsx(Flex, { alignItems: "center", height: "100%", ...containerProps, children: jsx(Text, { as: "span", overflow: "hidden", textOverflow: "ellipsis", wordBreak: "break-all", ...textProps, children: children }) }));
3507
+ if (href) {
3508
+ return (jsxs(Link, { href: href, target: "_blank", rel: "noopener noreferrer", _hover: {
3509
+ textDecoration: 'underline',
3510
+ }, children: [highlightedText, " ", jsx(Icon, { as: LuExternalLink })] }));
3511
+ }
3512
+ if (isCopyable) {
3513
+ return (jsx(TextWithCopy, { text: text, globalFilter: globalFilter, highlightedText: highlightedText }));
3514
+ }
3515
+ return jsx(Fragment, { children: highlightedText });
3516
+ };
3517
+ const TextCell = ({ text, href, onClick, isCopyable, isBadge, badgeColor, colorPalette,
3518
+ // Legacy props
3519
+ label, containerProps = {}, textProps = {}, children, }) => {
3520
+ // Get globalFilter from context
3521
+ // If not in DataTable context, will use default empty string from context
3522
+ const { globalFilter } = useDataTableContext();
3523
+ // Legacy API: if children is provided, use old behavior
3524
+ if (children !== undefined) {
3525
+ const displayText = typeof children === 'string' || typeof children === 'number'
3526
+ ? String(children)
3527
+ : children;
3528
+ const highlightedDisplayText = typeof displayText === 'string' || typeof displayText === 'number'
3529
+ ? highlightText(displayText, globalFilter)
3530
+ : displayText;
3531
+ if (label) {
3532
+ return (jsx(Flex, { alignItems: 'center', height: '100%', ...containerProps, children: jsx(Tooltip, { content: jsx(Text, { as: "span", overflow: "hidden", textOverflow: 'ellipsis', children: label }), children: jsx(Text, { as: "span", overflow: "hidden", textOverflow: 'ellipsis', wordBreak: 'break-all', ...textProps, children: highlightedDisplayText }) }) }));
3533
+ }
3534
+ return (jsx(Flex, { alignItems: 'center', height: '100%', ...containerProps, children: jsx(Text, { as: "span", overflow: "hidden", textOverflow: 'ellipsis', wordBreak: 'break-all', ...textProps, children: highlightedDisplayText }) }));
3535
+ }
3536
+ // New API: use text prop
3537
+ const displayValue = text ?? children;
3538
+ if (Array.isArray(displayValue)) {
3539
+ return (jsx(Flex, { gap: 2, flexWrap: "wrap", children: displayValue.map((item, index) => {
3540
+ const highlightedItem = highlightText(item, globalFilter);
3541
+ return (jsx(Badge, { colorPalette: colorPalette || badgeColor, children: highlightedItem }, index));
3542
+ }) }));
3543
+ }
3544
+ if (!!displayValue === false) {
3545
+ return (jsx(Text, { textOverflow: "ellipsis", whiteSpace: "nowrap", overflow: "hidden", wordBreak: "break-all", display: "flex", alignItems: "center", height: "100%", children: "-" }));
3546
+ }
3547
+ return (jsx(Box, { textOverflow: "ellipsis", whiteSpace: "nowrap", wordBreak: "break-all", overflow: "auto", display: "flex", alignItems: "center", height: "100%", children: jsx(RenderValue, { text: displayValue, href: href, onClick: onClick, isCopyable: isCopyable, isBadge: isBadge, badgeColor: badgeColor, colorPalette: colorPalette, globalFilter: globalFilter }) }));
3323
3548
  };
3324
3549
 
3325
3550
  const Tag = React.forwardRef(function Tag(props, ref) {
@@ -3482,7 +3707,7 @@ const snakeToLabel = (str) => {
3482
3707
  .join(" "); // Join with space
3483
3708
  };
3484
3709
 
3485
- const RecordDisplay = ({ object, boxProps, translate, prefix = "", }) => {
3710
+ const RecordDisplay = ({ object, boxProps, translate, prefix = '', }) => {
3486
3711
  const getColumn = ({ field }) => {
3487
3712
  if (translate !== undefined) {
3488
3713
  return translate.t(`${prefix}${field}`);
@@ -3492,8 +3717,9 @@ const RecordDisplay = ({ object, boxProps, translate, prefix = "", }) => {
3492
3717
  if (object === null) {
3493
3718
  return jsx(Fragment, { children: "null" });
3494
3719
  }
3495
- return (jsx(Grid, { rowGap: 1, padding: 1, overflow: "auto", ...boxProps, children: Object.entries(object).map(([field, value]) => {
3496
- return (jsxs(Grid, { columnGap: 2, gridTemplateColumns: "auto 1fr", children: [jsx(Text, { color: "colorPalette.400", children: getColumn({ field }) }), typeof value === "object" ? (jsx(RecordDisplay, { object: value, prefix: `${prefix}${field}.`, translate: translate })) : (jsx(Text, { children: JSON.stringify(value) }))] }, field));
3720
+ return (jsx(Grid, { rowGap: 1, padding: 1, overflow: 'auto', ...boxProps, children: Object.entries(object).map(([field, value], index) => {
3721
+ const uniqueKey = `${prefix}${field}-${index}`;
3722
+ return (jsxs(Grid, { columnGap: 2, gridTemplateColumns: 'auto 1fr', children: [jsx(Text, { color: 'colorPalette.400', children: getColumn({ field }) }), typeof value === 'object' && value !== null ? (jsx(RecordDisplay, { object: value, prefix: `${prefix}${field}.`, translate: translate })) : (jsx(Text, { children: JSON.stringify(value) }))] }, uniqueKey));
3497
3723
  }) }));
3498
3724
  };
3499
3725
 
@@ -3784,13 +4010,10 @@ const idPickerSanityCheck = (column, foreign_key) => {
3784
4010
  if (!!foreign_key == false) {
3785
4011
  throw new Error(`The key foreign_key does not exist in properties of column ${column} when using id-picker.`);
3786
4012
  }
3787
- const { table, column: foreignKeyColumn, display_column } = foreign_key;
4013
+ const { table, column: foreignKeyColumn } = foreign_key;
3788
4014
  if (!!table == false) {
3789
4015
  throw new Error(`The key table does not exist in properties of column ${table} when using id-picker.`);
3790
4016
  }
3791
- if (!!display_column == false) {
3792
- throw new Error(`The key display_column does not exist in properties of column ${column} when using id-picker.`);
3793
- }
3794
4017
  if (!!foreignKeyColumn == false) {
3795
4018
  throw new Error(`The key column does not exist in properties of column ${column} when using id-picker.`);
3796
4019
  }
@@ -3799,7 +4022,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
3799
4022
  showSubmitButton: true,
3800
4023
  showResetButton: true,
3801
4024
  showTitle: true,
3802
- }, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, insideDialog = false, }) => {
4025
+ }, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, formButtonLabels, insideDialog = false, }) => {
3803
4026
  const [isSuccess, setIsSuccess] = useState(false);
3804
4027
  const [isError, setIsError] = useState(false);
3805
4028
  const [isSubmiting, setIsSubmiting] = useState(false);
@@ -3889,6 +4112,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
3889
4112
  idPickerLabels,
3890
4113
  enumPickerLabels,
3891
4114
  filePickerLabels,
4115
+ formButtonLabels,
3892
4116
  ajvResolver: ajvResolver(schema),
3893
4117
  insideDialog,
3894
4118
  }, children: jsx(FormProvider, { ...form, children: children }) }));
@@ -3898,40 +4122,107 @@ function removeIndex(str) {
3898
4122
  return str.replace(/\.\d+\./g, ".");
3899
4123
  }
3900
4124
 
4125
+ /**
4126
+ * Custom hook to simplify i18n translation for form fields.
4127
+ * Automatically handles colLabel construction and removeIndex logic.
4128
+ *
4129
+ * @param column - The column name
4130
+ * @param prefix - The prefix for the field (usually empty string or parent path)
4131
+ * @returns Object with translation helper functions
4132
+ *
4133
+ * @example
4134
+ * ```tsx
4135
+ * const formI18n = useFormI18n(column, prefix);
4136
+ *
4137
+ * // Get field label
4138
+ * <Field label={formI18n.label()} />
4139
+ *
4140
+ * // Get error message
4141
+ * <Text>{formI18n.required()}</Text>
4142
+ *
4143
+ * // Get custom translation key
4144
+ * <Text>{formI18n.t('add_more')}</Text>
4145
+ *
4146
+ * // Access the raw colLabel
4147
+ * const colLabel = formI18n.colLabel;
4148
+ * ```
4149
+ */
4150
+ const useFormI18n$1 = (column, prefix = '', schema) => {
4151
+ const { translate } = useSchemaContext();
4152
+ const colLabel = `${prefix}${column}`;
4153
+ return {
4154
+ /**
4155
+ * The constructed column label (prefix + column)
4156
+ */
4157
+ colLabel,
4158
+ /**
4159
+ * Get the field label from schema title prop, or fall back to translation
4160
+ * Uses schema.title if available, otherwise: translate.t(removeIndex(`${colLabel}.field_label`))
4161
+ */
4162
+ label: (options) => {
4163
+ if (schema?.title) {
4164
+ return schema.title;
4165
+ }
4166
+ return translate.t(removeIndex(`${colLabel}.field_label`), options);
4167
+ },
4168
+ /**
4169
+ * Get the required error message translation
4170
+ * Equivalent to: translate.t(removeIndex(`${colLabel}.field_required`))
4171
+ */
4172
+ required: (options) => {
4173
+ return translate.t(removeIndex(`${colLabel}.field_required`), options);
4174
+ },
4175
+ /**
4176
+ * Get a translation for any custom key relative to the field
4177
+ * Equivalent to: translate.t(removeIndex(`${colLabel}.${key}`))
4178
+ *
4179
+ * @param key - The translation key suffix (e.g., 'add_more', 'total', etc.)
4180
+ * @param options - Optional translation options (e.g., defaultValue, interpolation variables)
4181
+ */
4182
+ t: (key, options) => {
4183
+ return translate.t(removeIndex(`${colLabel}.${key}`), options);
4184
+ },
4185
+ /**
4186
+ * Access to the original translate object for edge cases
4187
+ */
4188
+ translate,
4189
+ };
4190
+ };
4191
+
3901
4192
  const ArrayRenderer = ({ schema, column, prefix, }) => {
3902
- const { gridRow, gridColumn = "1/span 12", required, items } = schema;
4193
+ const { gridRow, gridColumn = '1/span 12', required, items } = schema;
3903
4194
  // @ts-expect-error TODO: find suitable types
3904
4195
  const { type } = items;
3905
- const { translate } = useSchemaContext();
3906
4196
  const colLabel = `${prefix}${column}`;
3907
4197
  const isRequired = required?.some((columnId) => columnId === column);
4198
+ const formI18n = useFormI18n$1(column, prefix, schema);
3908
4199
  const { formState: { errors }, setValue, watch, } = useFormContext();
3909
4200
  const fields = (watch(colLabel) ?? []);
3910
- return (jsxs(Flex, { gridRow, gridColumn, flexFlow: "column", gap: 2, children: [jsxs(Box, { as: "label", children: [`${translate.t(removeIndex(`${colLabel}.field_label`))}`, isRequired && jsx("span", { children: "*" })] }), jsx(Flex, { flexFlow: "column", gap: 2, children: fields.map((field, index) => (jsxs(Grid, { gridTemplateColumns: '1fr auto', gap: 2, bgColor: { base: "colorPalette.100", _dark: "colorPalette.900" }, p: 2, borderRadius: 4, borderWidth: 1, borderColor: {
3911
- base: "colorPalette.200",
3912
- _dark: "colorPalette.800",
3913
- }, children: [jsx(Grid, { gridTemplateColumns: "repeat(12, 1fr)", autoFlow: "row", children: jsx(SchemaRenderer, { column: `${index}`,
4201
+ return (jsxs(Flex, { gridRow, gridColumn, flexFlow: 'column', gap: 2, children: [jsxs(Box, { as: "label", children: [formI18n.label(), isRequired && jsx("span", { children: "*" })] }), jsx(Flex, { flexFlow: 'column', gap: 2, children: fields.map((field, index) => (jsxs(Grid, { gridTemplateColumns: '1fr auto', gap: 2, bgColor: { base: 'colorPalette.100', _dark: 'colorPalette.900' }, p: 2, borderRadius: 4, borderWidth: 1, borderColor: {
4202
+ base: 'colorPalette.200',
4203
+ _dark: 'colorPalette.800',
4204
+ }, children: [jsx(Grid, { gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: jsx(SchemaRenderer, { column: `${index}`,
3914
4205
  prefix: `${colLabel}.`,
3915
4206
  // @ts-expect-error find suitable types
3916
- schema: { showLabel: false, ...(items ?? {}) } }) }), jsx(Flex, { justifyContent: "end", children: jsx(Button$1, { variant: "ghost", onClick: () => {
4207
+ schema: { showLabel: false, ...(items ?? {}) } }) }), jsx(Flex, { justifyContent: 'end', children: jsx(Button$1, { variant: 'ghost', onClick: () => {
3917
4208
  setValue(colLabel, fields.filter((_, curIndex) => {
3918
4209
  return curIndex !== index;
3919
4210
  }));
3920
4211
  }, children: jsx(Icon, { children: jsx(CgTrash, {}) }) }) })] }, `${colLabel}.${index}`))) }), jsx(Flex, { children: jsx(Button$1, { onClick: () => {
3921
- if (type === "number") {
4212
+ if (type === 'number') {
3922
4213
  setValue(colLabel, [...fields, 0]);
3923
4214
  return;
3924
4215
  }
3925
- if (type === "string") {
3926
- setValue(colLabel, [...fields, ""]);
4216
+ if (type === 'string') {
4217
+ setValue(colLabel, [...fields, '']);
3927
4218
  return;
3928
4219
  }
3929
- if (type === "boolean") {
4220
+ if (type === 'boolean') {
3930
4221
  setValue(colLabel, [...fields, false]);
3931
4222
  return;
3932
4223
  }
3933
4224
  setValue(colLabel, [...fields, {}]);
3934
- }, children: translate.t(removeIndex(`${colLabel}.add`)) }) }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
4225
+ }, children: translate.t(removeIndex(`${colLabel}.add`)) }) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
3935
4226
  };
3936
4227
 
3937
4228
  const Field = React.forwardRef(function Field(props, ref) {
@@ -3941,15 +4232,13 @@ const Field = React.forwardRef(function Field(props, ref) {
3941
4232
 
3942
4233
  const BooleanPicker = ({ schema, column, prefix }) => {
3943
4234
  const { watch, formState: { errors }, setValue, } = useFormContext();
3944
- const { translate } = useSchemaContext();
3945
4235
  const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
3946
4236
  const isRequired = required?.some((columnId) => columnId === column);
3947
4237
  const colLabel = `${prefix}${column}`;
3948
4238
  const value = watch(colLabel);
3949
- return (jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
3950
- gridRow, errorText: errors[`${colLabel}`]
3951
- ? translate.t(removeIndex(`${colLabel}.field_required`))
3952
- : undefined, invalid: !!errors[colLabel], children: jsx(CheckboxCard, { checked: value, variant: 'surface', onChange: () => {
4239
+ const formI18n = useFormI18n$1(column, prefix, schema);
4240
+ return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4241
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsx(CheckboxCard, { checked: value, variant: 'surface', onChange: () => {
3953
4242
  setValue(colLabel, !value);
3954
4243
  } }) }));
3955
4244
  };
@@ -4065,76 +4354,12 @@ let DatePicker$1 = class DatePicker extends React__default.Component {
4065
4354
  }
4066
4355
  };
4067
4356
 
4068
- /**
4069
- * Custom hook to simplify i18n translation for form fields.
4070
- * Automatically handles colLabel construction and removeIndex logic.
4071
- *
4072
- * @param column - The column name
4073
- * @param prefix - The prefix for the field (usually empty string or parent path)
4074
- * @returns Object with translation helper functions
4075
- *
4076
- * @example
4077
- * ```tsx
4078
- * const formI18n = useFormI18n(column, prefix);
4079
- *
4080
- * // Get field label
4081
- * <Field label={formI18n.label()} />
4082
- *
4083
- * // Get error message
4084
- * <Text>{formI18n.required()}</Text>
4085
- *
4086
- * // Get custom translation key
4087
- * <Text>{formI18n.t('add_more')}</Text>
4088
- *
4089
- * // Access the raw colLabel
4090
- * const colLabel = formI18n.colLabel;
4091
- * ```
4092
- */
4093
- const useFormI18n = (column, prefix = "") => {
4094
- const { translate } = useSchemaContext();
4095
- const colLabel = `${prefix}${column}`;
4096
- return {
4097
- /**
4098
- * The constructed column label (prefix + column)
4099
- */
4100
- colLabel,
4101
- /**
4102
- * Get the field label translation
4103
- * Equivalent to: translate.t(removeIndex(`${colLabel}.field_label`))
4104
- */
4105
- label: (options) => {
4106
- return translate.t(removeIndex(`${colLabel}.field_label`), options);
4107
- },
4108
- /**
4109
- * Get the required error message translation
4110
- * Equivalent to: translate.t(removeIndex(`${colLabel}.field_required`))
4111
- */
4112
- required: (options) => {
4113
- return translate.t(removeIndex(`${colLabel}.field_required`), options);
4114
- },
4115
- /**
4116
- * Get a translation for any custom key relative to the field
4117
- * Equivalent to: translate.t(removeIndex(`${colLabel}.${key}`))
4118
- *
4119
- * @param key - The translation key suffix (e.g., 'add_more', 'total', etc.)
4120
- * @param options - Optional translation options (e.g., defaultValue, interpolation variables)
4121
- */
4122
- t: (key, options) => {
4123
- return translate.t(removeIndex(`${colLabel}.${key}`), options);
4124
- },
4125
- /**
4126
- * Access to the original translate object for edge cases
4127
- */
4128
- translate,
4129
- };
4130
- };
4131
-
4132
4357
  dayjs.extend(utc);
4133
4358
  dayjs.extend(timezone);
4134
4359
  const DatePicker = ({ column, schema, prefix }) => {
4135
4360
  const { watch, formState: { errors }, setValue, } = useFormContext();
4136
4361
  const { timezone, dateTimePickerLabels, insideDialog } = useSchemaContext();
4137
- const formI18n = useFormI18n(column, prefix);
4362
+ const formI18n = useFormI18n$1(column, prefix, schema);
4138
4363
  const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
4139
4364
  const isRequired = required?.some((columnId) => columnId === column);
4140
4365
  const colLabel = formI18n.colLabel;
@@ -4252,7 +4477,7 @@ dayjs.extend(timezone);
4252
4477
  const DateRangePicker = ({ column, schema, prefix, }) => {
4253
4478
  const { watch, formState: { errors }, setValue, } = useFormContext();
4254
4479
  const { timezone, insideDialog } = useSchemaContext();
4255
- const formI18n = useFormI18n(column, prefix);
4480
+ const formI18n = useFormI18n$1(column, prefix);
4256
4481
  const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
4257
4482
  const isRequired = required?.some((columnId) => columnId === column);
4258
4483
  const colLabel = formI18n.colLabel;
@@ -4350,7 +4575,7 @@ const DateRangePicker = ({ column, schema, prefix, }) => {
4350
4575
  const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLimit = false, }) => {
4351
4576
  const { watch, formState: { errors }, setValue, } = useFormContext();
4352
4577
  const { enumPickerLabels, insideDialog } = useSchemaContext();
4353
- const formI18n = useFormI18n(column, prefix);
4578
+ const formI18n = useFormI18n$1(column, prefix, schema);
4354
4579
  const { required, variant } = schema;
4355
4580
  const isRequired = required?.some((columnId) => columnId === column);
4356
4581
  const { gridColumn = 'span 12', gridRow = 'span 1', renderDisplay } = schema;
@@ -4994,7 +5219,7 @@ function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly =
4994
5219
  const FilePicker = ({ column, schema, prefix }) => {
4995
5220
  const { setValue, formState: { errors }, watch, } = useFormContext();
4996
5221
  const { filePickerLabels } = useSchemaContext();
4997
- const formI18n = useFormI18n(column, prefix);
5222
+ const formI18n = useFormI18n$1(column, prefix);
4998
5223
  const { required, gridColumn = 'span 12', gridRow = 'span 1', type, } = schema;
4999
5224
  const isRequired = required?.some((columnId) => columnId === column);
5000
5225
  const isSingleSelect = type === 'string';
@@ -5070,7 +5295,7 @@ const FilePicker = ({ column, schema, prefix }) => {
5070
5295
  const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5071
5296
  const { setValue, formState: { errors }, watch, } = useFormContext();
5072
5297
  const { filePickerLabels } = useSchemaContext();
5073
- const formI18n = useFormI18n(column, prefix);
5298
+ const formI18n = useFormI18n$1(column, prefix);
5074
5299
  const { required, gridColumn = 'span 12', gridRow = 'span 1', filePicker, type, } = schema;
5075
5300
  const isRequired = required?.some((columnId) => columnId === column);
5076
5301
  const isSingleSelect = type === 'string';
@@ -5211,13 +5436,18 @@ const getTableData = async ({ serverUrl, in_table, searching = "", where = [], l
5211
5436
  }
5212
5437
  };
5213
5438
 
5439
+ // Default renderDisplay function that stringifies JSON
5440
+ const defaultRenderDisplay = (item) => {
5441
+ return JSON.stringify(item);
5442
+ };
5443
+
5214
5444
  const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
5215
5445
  const { watch, getValues, formState: { errors }, setValue, } = useFormContext();
5216
5446
  const { serverUrl, idMap, setIdMap, idPickerLabels, insideDialog } = useSchemaContext();
5217
- const formI18n = useFormI18n(column, prefix);
5447
+ const formI18n = useFormI18n$1(column, prefix, schema);
5218
5448
  const { required, gridColumn = 'span 12', gridRow = 'span 1', renderDisplay, foreign_key, } = schema;
5219
5449
  const isRequired = required?.some((columnId) => columnId === column);
5220
- const { table, column: column_ref, display_column, customQueryFn, } = foreign_key;
5450
+ const { table, column: column_ref, customQueryFn, } = foreign_key;
5221
5451
  const [searchText, setSearchText] = useState('');
5222
5452
  const [debouncedSearchText, setDebouncedSearchText] = useState('');
5223
5453
  const [limit] = useState(50); // Increased limit for combobox
@@ -5295,15 +5525,19 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
5295
5525
  // Check if we're currently searching (user typed but debounce hasn't fired yet)
5296
5526
  const isSearching = searchText !== debouncedSearchText;
5297
5527
  // Transform data for combobox collection
5528
+ // label is used for filtering/searching (must be a string)
5529
+ // raw item is stored for custom rendering
5298
5530
  const comboboxItems = useMemo(() => {
5299
- return dataList.map((item) => ({
5300
- label: !!renderDisplay === true
5301
- ? String(renderDisplay(item))
5302
- : String(item[display_column] ?? ''),
5303
- value: String(item[column_ref]),
5304
- raw: item,
5305
- }));
5306
- }, [dataList, display_column, column_ref, renderDisplay]);
5531
+ const renderFn = renderDisplay || defaultRenderDisplay;
5532
+ return dataList.map((item) => {
5533
+ const rendered = renderFn(item);
5534
+ return {
5535
+ label: typeof rendered === 'string' ? rendered : JSON.stringify(item), // Use string for filtering
5536
+ value: String(item[column_ref]),
5537
+ raw: item,
5538
+ };
5539
+ });
5540
+ }, [dataList, column_ref, renderDisplay]);
5307
5541
  // Use filter hook for combobox
5308
5542
  const { contains } = useFilter({ sensitivity: 'base' });
5309
5543
  // Create collection for combobox
@@ -5349,9 +5583,9 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
5349
5583
  return (jsx(Tag, { closable: true, onClick: () => {
5350
5584
  const newValue = currentValue.filter((itemId) => itemId !== id);
5351
5585
  setValue(colLabel, newValue);
5352
- }, children: !!renderDisplay === true
5586
+ }, children: renderDisplay
5353
5587
  ? renderDisplay(item)
5354
- : item[display_column] }, id));
5588
+ : defaultRenderDisplay(item) }, id));
5355
5589
  }) })), jsxs(Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", positioning: insideDialog
5356
5590
  ? { strategy: 'fixed', hideWhenDetached: true }
5357
5591
  : undefined, children: [jsxs(Combobox.Control, { children: [jsx(Combobox.Input, { placeholder: idPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxs(Combobox.IndicatorGroup, { children: [(isFetching || isLoading || isPending) && jsx(Spinner, { size: "xs" }), isError && (jsx(Icon, { color: "fg.error", children: jsx(BiError, {}) })), !isMultiple && currentValue.length > 0 && (jsx(Combobox.ClearTrigger, { onClick: () => {
@@ -5362,13 +5596,17 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
5362
5596
  ? idPickerLabels?.emptySearchResult ??
5363
5597
  formI18n.t('empty_search_result')
5364
5598
  : idPickerLabels?.initialResults ??
5365
- formI18n.t('initial_results') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: formI18n.t('loading_failed') })) : isFetching || isLoading || isPending || isSearching ? (
5599
+ formI18n.t('initial_results') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: !!renderDisplay === true
5600
+ ? renderDisplay(item.raw)
5601
+ : item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: formI18n.t('loading_failed') })) : isFetching || isLoading || isPending || isSearching ? (
5366
5602
  // Show skeleton items to prevent UI shift
5367
5603
  jsx(Fragment, { children: Array.from({ length: 5 }).map((_, index) => (jsx(Flex, { p: 2, align: "center", gap: 2, children: jsx(Skeleton, { height: "20px", flex: "1" }) }, `skeleton-${index}`))) })) : collection.items.length === 0 ? (jsx(Combobox.Empty, { children: searchText
5368
5604
  ? idPickerLabels?.emptySearchResult ??
5369
5605
  formI18n.t('empty_search_result')
5370
5606
  : idPickerLabels?.initialResults ??
5371
- formI18n.t('initial_results') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) }) }))] })] }));
5607
+ formI18n.t('initial_results') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: !!renderDisplay === true
5608
+ ? renderDisplay(item.raw)
5609
+ : item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) }) }))] })] }));
5372
5610
  };
5373
5611
 
5374
5612
  const NumberInputRoot = React.forwardRef(function NumberInput$1(props, ref) {
@@ -5436,15 +5674,15 @@ const extractErrorMessage = (error) => {
5436
5674
 
5437
5675
  const NumberInputField = ({ schema, column, prefix, }) => {
5438
5676
  const { setValue, formState: { errors }, watch, } = useFormContext();
5439
- const { translate } = useSchemaContext();
5440
5677
  const { required, gridColumn = 'span 12', gridRow = 'span 1', numberStorageType = 'number', } = schema;
5441
5678
  const isRequired = required?.some((columnId) => columnId === column);
5442
5679
  const colLabel = `${prefix}${column}`;
5443
5680
  const value = watch(`${colLabel}`);
5444
5681
  const fieldError = getFieldError(errors, colLabel);
5445
- return (jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn, gridRow, errorText: fieldError
5682
+ const formI18n = useFormI18n$1(column, prefix, schema);
5683
+ return (jsx(Field, { label: formI18n.label(), required: isRequired, gridColumn, gridRow, errorText: fieldError
5446
5684
  ? fieldError.includes('required')
5447
- ? translate.t(removeIndex(`${colLabel}.field_required`))
5685
+ ? formI18n.required()
5448
5686
  : fieldError
5449
5687
  : undefined, invalid: !!fieldError, children: jsx(NumberInputRoot, { value: value, onValueChange: (details) => {
5450
5688
  // Store as string or number based on configuration, default to number
@@ -5457,14 +5695,14 @@ const NumberInputField = ({ schema, column, prefix, }) => {
5457
5695
 
5458
5696
  const ObjectInput = ({ schema, column, prefix }) => {
5459
5697
  const { properties, gridColumn = 'span 12', gridRow = 'span 1', required, showLabel = true, } = schema;
5460
- const { translate } = useSchemaContext();
5461
5698
  const colLabel = `${prefix}${column}`;
5462
5699
  const isRequired = required?.some((columnId) => columnId === column);
5700
+ const formI18n = useFormI18n$1(column, prefix, schema);
5463
5701
  const { formState: { errors }, } = useFormContext();
5464
5702
  if (properties === undefined) {
5465
5703
  throw new Error(`properties is undefined when using ObjectInput`);
5466
5704
  }
5467
- return (jsxs(Box, { gridRow, gridColumn, children: [showLabel && (jsxs(Box, { as: "label", children: [`${translate.t(removeIndex(`${colLabel}.field_label`))}`, isRequired && jsx("span", { children: "*" })] })), jsx(Grid, { bgColor: { base: 'colorPalette.100', _dark: 'colorPalette.900' }, p: 2, borderRadius: 4, borderWidth: 1, borderColor: {
5705
+ return (jsxs(Box, { gridRow, gridColumn, children: [showLabel && (jsxs(Box, { as: "label", children: [formI18n.label(), isRequired && jsx("span", { children: "*" })] })), jsx(Grid, { bgColor: { base: 'colorPalette.100', _dark: 'colorPalette.900' }, p: 2, borderRadius: 4, borderWidth: 1, borderColor: {
5468
5706
  base: 'colorPalette.200',
5469
5707
  _dark: 'colorPalette.800',
5470
5708
  }, gap: "4", padding: '4', gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: Object.keys(properties ?? {}).map((key) => {
@@ -5474,10 +5712,10 @@ const ObjectInput = ({ schema, column, prefix }) => {
5474
5712
  prefix: `${prefix}${column}.`,
5475
5713
  properties,
5476
5714
  parentRequired: required }, `form-${colLabel}-${key}`));
5477
- }) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
5715
+ }) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
5478
5716
  };
5479
5717
 
5480
- const RecordInput$1 = ({ column, schema, prefix }) => {
5718
+ const RecordInput = ({ column, schema, prefix }) => {
5481
5719
  const { formState: { errors }, setValue, getValues, } = useFormContext();
5482
5720
  const { translate } = useSchemaContext();
5483
5721
  const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
@@ -5486,9 +5724,8 @@ const RecordInput$1 = ({ column, schema, prefix }) => {
5486
5724
  const [showNewEntries, setShowNewEntries] = useState(false);
5487
5725
  const [newKey, setNewKey] = useState();
5488
5726
  const [newValue, setNewValue] = useState();
5489
- return (jsxs(Field, { label: `${translate.t(`${column}.field_label`)}`, required: isRequired, alignItems: 'stretch', gridColumn, gridRow, errorText: errors[`${column}`]
5490
- ? translate.t(`${column}.field_required`)
5491
- : undefined, invalid: !!errors[column], children: [entries.map(([key, value]) => {
5727
+ const formI18n = useFormI18n$1(column, prefix, schema);
5728
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn, gridRow, errorText: errors[`${column}`] ? formI18n.required() : undefined, invalid: !!errors[column], children: [entries.map(([key, value]) => {
5492
5729
  return (jsxs(Grid, { templateColumns: '1fr 1fr auto', gap: 1, children: [jsx(Input, { value: key, onChange: (e) => {
5493
5730
  const filtered = entries.filter(([target]) => {
5494
5731
  return target !== key;
@@ -5533,12 +5770,12 @@ const RecordInput$1 = ({ column, schema, prefix }) => {
5533
5770
 
5534
5771
  const StringInputField = ({ column, schema, prefix, }) => {
5535
5772
  const { register, formState: { errors }, } = useFormContext();
5536
- const { translate } = useSchemaContext();
5537
5773
  const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
5538
5774
  const isRequired = required?.some((columnId) => columnId === column);
5539
5775
  const colLabel = `${prefix}${column}`;
5540
5776
  const fieldError = getFieldError(errors, colLabel);
5541
- return (jsx(Fragment, { children: jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn, gridRow: gridRow, errorText: fieldError, invalid: !!fieldError, children: jsx(Input, { ...register(`${colLabel}`, { required: isRequired }), autoComplete: "off" }) }) }));
5777
+ const formI18n = useFormI18n$1(column, prefix, schema);
5778
+ return (jsx(Fragment, { children: jsx(Field, { label: formI18n.label(), required: isRequired, gridColumn: gridColumn, gridRow: gridRow, errorText: fieldError, invalid: !!fieldError, children: jsx(Input, { ...register(`${colLabel}`, { required: isRequired }), autoComplete: "off" }) }) }));
5542
5779
  };
5543
5780
 
5544
5781
  const RadioCardItem = React.forwardRef(function RadioCardItem(props, ref) {
@@ -5723,17 +5960,17 @@ Textarea.displayName = "Textarea";
5723
5960
 
5724
5961
  const TextAreaInput = ({ column, schema, prefix, }) => {
5725
5962
  const { register, formState: { errors }, } = useFormContext();
5726
- const { translate } = useSchemaContext();
5727
5963
  const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
5728
5964
  const isRequired = required?.some((columnId) => columnId === column);
5729
5965
  const colLabel = `${prefix}${column}`;
5730
5966
  const form = useFormContext();
5731
5967
  const { setValue, watch } = form;
5732
5968
  const fieldError = getFieldError(errors, colLabel);
5969
+ const formI18n = useFormI18n$1(column, prefix, schema);
5733
5970
  const watchValue = watch(colLabel);
5734
- return (jsx(Fragment, { children: jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn ?? 'span 4', gridRow: gridRow ?? 'span 1', display: "grid", errorText: fieldError
5971
+ return (jsx(Fragment, { children: jsx(Field, { label: formI18n.label(), required: isRequired, gridColumn: gridColumn ?? 'span 4', gridRow: gridRow ?? 'span 1', display: "grid", errorText: fieldError
5735
5972
  ? fieldError.includes('required')
5736
- ? translate.t(removeIndex(`${colLabel}.field_required`))
5973
+ ? formI18n.required()
5737
5974
  : fieldError
5738
5975
  : undefined, invalid: !!fieldError, children: jsx(Textarea, { value: watchValue, onChange: (value) => setValue(colLabel, value) }) }) }));
5739
5976
  };
@@ -5747,7 +5984,7 @@ meridiemLabel: _meridiemLabel = {
5747
5984
  pm: 'pm',
5748
5985
  },
5749
5986
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
5750
- onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedDate, }) {
5987
+ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedDate, portalled = true, }) {
5751
5988
  // Generate time options (every 15 minutes)
5752
5989
  const timeOptions = useMemo(() => {
5753
5990
  const options = [];
@@ -5770,12 +6007,10 @@ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedD
5770
6007
  for (let h = 1; h <= 12; h++) {
5771
6008
  for (let m = 0; m < 60; m += 15) {
5772
6009
  const hour24 = mer === 'am' ? (h === 12 ? 0 : h) : h === 12 ? 12 : h + 12;
5773
- const timeStr = dayjs()
5774
- .tz(timezone)
5775
- .hour(hour24)
5776
- .minute(m)
5777
- .format('HH:mmZ');
5778
- const displayTime = dayjs(`1970-01-01T${timeStr}`, 'HH:mmZ').format('hh:mm a');
6010
+ // Format time directly without using dayjs with dummy dates
6011
+ const formattedHour = h.toString().padStart(2, '0');
6012
+ const formattedMinute = m.toString().padStart(2, '0');
6013
+ const displayTime = `${formattedHour}:${formattedMinute} ${mer}`;
5779
6014
  // Filter out times that would result in negative duration (only when dates are the same)
5780
6015
  if (startDateTime && selectedDate && shouldFilterByDate) {
5781
6016
  const selectedDateObj = dayjs(selectedDate).tz(timezone);
@@ -5788,8 +6023,8 @@ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedD
5788
6023
  continue; // Skip this option as it would result in negative duration
5789
6024
  }
5790
6025
  }
5791
- // Calculate and append duration if startTime is provided
5792
- let label = displayTime;
6026
+ // Calculate duration if startTime is provided
6027
+ let durationText;
5793
6028
  if (startDateTime && selectedDate) {
5794
6029
  const selectedDateObj = dayjs(selectedDate).tz(timezone);
5795
6030
  const optionDateTime = selectedDateObj
@@ -5802,21 +6037,30 @@ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedD
5802
6037
  const diffMs = optionDateTime.diff(startDateTime);
5803
6038
  const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
5804
6039
  const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
5805
- if (diffHours > 0 || diffMinutes > 0) {
5806
- const diffText = diffHours > 0
5807
- ? `${diffHours}h ${diffMinutes}m`
5808
- : `${diffMinutes}m`;
5809
- label = `${displayTime} (+${diffText})`;
6040
+ const diffSeconds = Math.floor((diffMs % (1000 * 60)) / 1000);
6041
+ if (diffHours > 0 || diffMinutes > 0 || diffSeconds > 0) {
6042
+ let diffText = '';
6043
+ if (diffHours > 0) {
6044
+ diffText = `${diffHours}h ${diffMinutes}m`;
6045
+ }
6046
+ else if (diffMinutes > 0) {
6047
+ diffText = `${diffMinutes}m ${diffSeconds}s`;
6048
+ }
6049
+ else {
6050
+ diffText = `${diffSeconds}s`;
6051
+ }
6052
+ durationText = `+${diffText}`;
5810
6053
  }
5811
6054
  }
5812
6055
  }
5813
6056
  options.push({
5814
- label,
6057
+ label: displayTime,
5815
6058
  value: `${h}:${m.toString().padStart(2, '0')}:${mer}`,
5816
6059
  hour: h,
5817
6060
  minute: m,
5818
6061
  meridiem: mer,
5819
6062
  searchText: displayTime, // Use base time without duration for searching
6063
+ durationText,
5820
6064
  });
5821
6065
  }
5822
6066
  }
@@ -5830,22 +6074,6 @@ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedD
5830
6074
  itemToValue: (item) => item.value,
5831
6075
  filter: contains,
5832
6076
  });
5833
- // Track input mode vs display mode
5834
- const [isInputMode, setIsInputMode] = useState(false);
5835
- const [inputValue, setInputValue] = useState('');
5836
- const inputRef = useRef(null);
5837
- // Switch to display mode when value is selected
5838
- useEffect(() => {
5839
- if (hour !== null && minute !== null && meridiem !== null) {
5840
- setIsInputMode(false);
5841
- }
5842
- }, [hour, minute, meridiem]);
5843
- // Focus input when switching to input mode
5844
- useEffect(() => {
5845
- if (isInputMode && inputRef.current) {
5846
- inputRef.current.focus();
5847
- }
5848
- }, [isInputMode]);
5849
6077
  // Get current value string for combobox
5850
6078
  const currentValue = useMemo(() => {
5851
6079
  if (hour === null || minute === null || meridiem === null) {
@@ -5853,14 +6081,14 @@ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedD
5853
6081
  }
5854
6082
  return `${hour}:${minute.toString().padStart(2, '0')}:${meridiem}`;
5855
6083
  }, [hour, minute, meridiem]);
5856
- // INPUT MODE: Show raw input text (no duration)
5857
- const inputModeText = useMemo(() => {
5858
- return inputValue;
5859
- }, [inputValue]);
5860
- // DISPLAY MODE: Show selected value with duration
5861
- const displayModeText = useMemo(() => {
5862
- if (hour === null || minute === null || meridiem === null) {
5863
- return '';
6084
+ // Calculate duration difference
6085
+ const durationDiff = useMemo(() => {
6086
+ if (!startTime ||
6087
+ !selectedDate ||
6088
+ hour === null ||
6089
+ minute === null ||
6090
+ meridiem === null) {
6091
+ return null;
5864
6092
  }
5865
6093
  const hour24 = meridiem === 'am'
5866
6094
  ? hour === 12
@@ -5869,45 +6097,42 @@ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedD
5869
6097
  : hour === 12
5870
6098
  ? 12
5871
6099
  : hour + 12;
5872
- const timeStr = dayjs()
5873
- .tz(timezone)
6100
+ const startDateObj = dayjs(startTime).tz(timezone);
6101
+ const selectedDateObj = dayjs(selectedDate).tz(timezone);
6102
+ const currentDateTime = selectedDateObj
5874
6103
  .hour(hour24)
5875
6104
  .minute(minute)
5876
- .format('HH:mmZ');
5877
- const timeDisplay = dayjs(`1970-01-01T${timeStr}`, 'HH:mmZ').format('hh:mm a');
5878
- // Add duration if startTime is provided
5879
- if (startTime && selectedDate) {
5880
- const startDateObj = dayjs(startTime).tz(timezone);
5881
- const selectedDateObj = dayjs(selectedDate).tz(timezone);
5882
- const currentDateTime = selectedDateObj
5883
- .hour(hour24)
5884
- .minute(minute)
5885
- .second(0)
5886
- .millisecond(0);
5887
- if (startDateObj.isValid() && currentDateTime.isValid()) {
5888
- const diffMs = currentDateTime.diff(startDateObj);
5889
- if (diffMs >= 0) {
5890
- const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
5891
- const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
5892
- if (diffHours > 0 || diffMinutes > 0) {
5893
- const diffText = diffHours > 0
5894
- ? `${diffHours}h ${diffMinutes}m`
5895
- : `${diffMinutes}m`;
5896
- return `${timeDisplay} (+${diffText})`;
5897
- }
5898
- }
6105
+ .second(0)
6106
+ .millisecond(0);
6107
+ if (!startDateObj.isValid() || !currentDateTime.isValid()) {
6108
+ return null;
6109
+ }
6110
+ const diffMs = currentDateTime.diff(startDateObj);
6111
+ if (diffMs < 0) {
6112
+ return null;
6113
+ }
6114
+ const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
6115
+ const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
6116
+ const diffSeconds = Math.floor((diffMs % (1000 * 60)) / 1000);
6117
+ if (diffHours > 0 || diffMinutes > 0 || diffSeconds > 0) {
6118
+ let diffText = '';
6119
+ if (diffHours > 0) {
6120
+ diffText = `${diffHours}h ${diffMinutes}m`;
6121
+ }
6122
+ else if (diffMinutes > 0) {
6123
+ diffText = `${diffMinutes}m ${diffSeconds}s`;
5899
6124
  }
6125
+ else {
6126
+ diffText = `${diffSeconds}s`;
6127
+ }
6128
+ return `+${diffText}`;
5900
6129
  }
5901
- return timeDisplay;
5902
- }, [hour, minute, meridiem, timezone, startTime, selectedDate]);
5903
- // Choose text based on mode
5904
- const displayText = isInputMode ? inputModeText : displayModeText;
6130
+ return null;
6131
+ }, [hour, minute, meridiem, startTime, selectedDate, timezone]);
5905
6132
  const handleClear = () => {
5906
6133
  setHour(null);
5907
6134
  setMinute(null);
5908
6135
  setMeridiem(null);
5909
- setIsInputMode(false);
5910
- setInputValue('');
5911
6136
  filter(''); // Reset filter to show all options
5912
6137
  onChange({ hour: null, minute: null, meridiem: null });
5913
6138
  };
@@ -5922,8 +6147,6 @@ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedD
5922
6147
  setHour(selectedOption.hour);
5923
6148
  setMinute(selectedOption.minute);
5924
6149
  setMeridiem(selectedOption.meridiem);
5925
- setIsInputMode(false); // Switch to display mode
5926
- setInputValue('');
5927
6150
  filter(''); // Reset filter after selection
5928
6151
  onChange({
5929
6152
  hour: selectedOption.hour,
@@ -5932,41 +6155,16 @@ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedD
5932
6155
  });
5933
6156
  }
5934
6157
  };
5935
- // Handle Enter key to select first filtered option
5936
- const handleKeyDown = (e) => {
5937
- if (e.key === 'Enter' && collection.items.length > 0) {
5938
- e.preventDefault();
5939
- const firstOption = collection.items[0];
5940
- if (firstOption) {
5941
- const selectedOption = timeOptions.find((opt) => opt.value === firstOption.value);
5942
- if (selectedOption) {
5943
- setHour(selectedOption.hour);
5944
- setMinute(selectedOption.minute);
5945
- setMeridiem(selectedOption.meridiem);
5946
- setIsInputMode(false); // Switch to display mode
5947
- setInputValue('');
5948
- filter('');
5949
- onChange({
5950
- hour: selectedOption.hour,
5951
- minute: selectedOption.minute,
5952
- meridiem: selectedOption.meridiem,
5953
- });
5954
- }
5955
- }
5956
- }
5957
- };
5958
- const handleInputValueChange = (details) => {
5959
- const inputValue = details.inputValue.trim();
5960
- setInputValue(inputValue);
5961
- setIsInputMode(true); // Switch to input mode
6158
+ // Parse input value and update state
6159
+ const parseAndCommitInput = (value) => {
6160
+ const trimmedValue = value.trim();
5962
6161
  // Filter the collection based on input
5963
- filter(inputValue);
5964
- if (!inputValue) {
5965
- setIsInputMode(false);
6162
+ filter(trimmedValue);
6163
+ if (!trimmedValue) {
5966
6164
  return;
5967
6165
  }
5968
6166
  // Try to parse custom input using explicit regex patterns
5969
- const normalized = inputValue.toLowerCase().replace(/\s+/g, '');
6167
+ const normalized = trimmedValue.toLowerCase().replace(/\s+/g, '');
5970
6168
  // Pattern 1: 12-hour format with meridiem (e.g., "930pm", "1230am", "9:30pm", "12:30am")
5971
6169
  // Matches: 1-2 digits hour, optional colon, 2 digits minute, am/pm
5972
6170
  const pattern12HourWithMeridiem = /^(\d{1,2}):?(\d{2})(am|pm)$/;
@@ -5977,29 +6175,43 @@ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedD
5977
6175
  const parsedMeridiem = match12Hour[3];
5978
6176
  // Validate hour (1-12)
5979
6177
  if (parsedHour < 1 || parsedHour > 12) {
6178
+ // Parse failed, select first result
6179
+ selectFirstResult();
5980
6180
  return;
5981
6181
  }
5982
6182
  // Validate minute (0-59)
5983
- const validMinute = parsedMinute > 59 ? 0 : parsedMinute;
6183
+ if (parsedMinute < 0 || parsedMinute > 59) {
6184
+ // Parse failed, select first result
6185
+ selectFirstResult();
6186
+ return;
6187
+ }
5984
6188
  setHour(parsedHour);
5985
- setMinute(validMinute);
6189
+ setMinute(parsedMinute);
5986
6190
  setMeridiem(parsedMeridiem);
5987
6191
  onChange({
5988
6192
  hour: parsedHour,
5989
- minute: validMinute,
6193
+ minute: parsedMinute,
5990
6194
  meridiem: parsedMeridiem,
5991
6195
  });
5992
6196
  return;
5993
6197
  }
5994
6198
  // Pattern 2: 24-hour format (e.g., "2130", "09:30", "21:30")
5995
6199
  // Matches: 1-2 digits hour, optional colon, 2 digits minute
5996
- const pattern24Hour = /^(\d{2}):?(\d{2})$/;
6200
+ const pattern24Hour = /^(\d{1,2}):?(\d{2})$/;
5997
6201
  const match24Hour = normalized.match(pattern24Hour);
5998
6202
  if (match24Hour) {
5999
6203
  let parsedHour = parseInt(match24Hour[1], 10);
6000
6204
  const parsedMinute = parseInt(match24Hour[2], 10);
6001
6205
  // Validate hour (0-23)
6002
- if (parsedHour > 23) {
6206
+ if (parsedHour < 0 || parsedHour > 23) {
6207
+ // Parse failed, select first result
6208
+ selectFirstResult();
6209
+ return;
6210
+ }
6211
+ // Validate minute (0-59)
6212
+ if (parsedMinute < 0 || parsedMinute > 59) {
6213
+ // Parse failed, select first result
6214
+ selectFirstResult();
6003
6215
  return;
6004
6216
  }
6005
6217
  // Convert 24-hour to 12-hour format
@@ -6019,32 +6231,72 @@ onChange = (_newValue) => { }, timezone = 'Asia/Hong_Kong', startTime, selectedD
6019
6231
  else {
6020
6232
  parsedMeridiem = 'am';
6021
6233
  }
6022
- // Validate minute (0-59)
6023
- const validMinute = parsedMinute > 59 ? 0 : parsedMinute;
6024
6234
  setHour(parsedHour);
6025
- setMinute(validMinute);
6235
+ setMinute(parsedMinute);
6026
6236
  setMeridiem(parsedMeridiem);
6027
6237
  onChange({
6028
6238
  hour: parsedHour,
6029
- minute: validMinute,
6239
+ minute: parsedMinute,
6030
6240
  meridiem: parsedMeridiem,
6031
6241
  });
6032
6242
  return;
6033
6243
  }
6244
+ // Parse failed, select first result
6245
+ selectFirstResult();
6246
+ };
6247
+ // Select first result from filtered collection
6248
+ const selectFirstResult = () => {
6249
+ if (collection.items.length > 0) {
6250
+ const firstItem = collection.items[0];
6251
+ setHour(firstItem.hour);
6252
+ setMinute(firstItem.minute);
6253
+ setMeridiem(firstItem.meridiem);
6254
+ filter(''); // Reset filter after selection
6255
+ onChange({
6256
+ hour: firstItem.hour,
6257
+ minute: firstItem.minute,
6258
+ meridiem: firstItem.meridiem,
6259
+ });
6260
+ }
6261
+ };
6262
+ const handleInputValueChange = (details) => {
6263
+ // Filter the collection based on input, but don't parse yet
6264
+ filter(details.inputValue);
6265
+ };
6266
+ const handleFocus = (e) => {
6267
+ // Select all text when focusing
6268
+ e.target.select();
6269
+ };
6270
+ const handleBlur = (e) => {
6271
+ // Parse and commit the input value when losing focus
6272
+ const inputValue = e.target.value;
6273
+ if (inputValue) {
6274
+ parseAndCommitInput(inputValue);
6275
+ }
6276
+ };
6277
+ const handleKeyDown = (e) => {
6278
+ // Commit input on Enter key
6279
+ if (e.key === 'Enter') {
6280
+ e.preventDefault();
6281
+ const inputValue = e.currentTarget.value;
6282
+ if (inputValue) {
6283
+ parseAndCommitInput(inputValue);
6284
+ }
6285
+ // Blur the input
6286
+ e.currentTarget?.blur();
6287
+ }
6034
6288
  };
6035
- return (jsxs(Grid, { justifyContent: 'center', alignItems: 'center', templateColumns: '200px auto', gap: "2", width: "auto", minWidth: "250px", children: [jsxs(Combobox.Root, { collection: collection, value: currentValue ? [currentValue] : [], onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, allowCustomValue: true, selectionBehavior: "replace", openOnClick: true, width: "100%", children: [jsxs(Combobox.Control, { children: [isInputMode ? (jsx(InputGroup$1, { startElement: jsx(BsClock, {}), children: jsx(Combobox.Input, { ref: inputRef, placeholder: "Select time", value: displayText, onKeyDown: handleKeyDown }) })) : (jsxs(Grid, { templateColumns: "auto 1fr auto", alignItems: "center", gap: 2, width: "100%", minHeight: "40px", px: 3, border: "1px solid", borderColor: "gray.200", borderRadius: "md", cursor: "pointer", onClick: () => setIsInputMode(true), children: [jsx(Icon, { children: jsx(BsClock, {}) }), jsx(Text, { fontSize: "sm", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: displayText || 'Select time' }), jsx(Combobox.Trigger, { onClick: (e) => {
6036
- e.stopPropagation();
6037
- setIsInputMode(true);
6038
- } })] })), isInputMode && (jsxs(Combobox.IndicatorGroup, { children: [jsx(Combobox.ClearTrigger, {}), jsx(Combobox.Trigger, {})] }))] }), jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [jsx(Combobox.Empty, { children: "No time found" }), collection.items.map((item) => (jsxs(Combobox.Item, { item: item, children: [item.label, jsx(Combobox.ItemIndicator, {})] }, item.value)))] }) }) })] }), jsx(Button$1, { onClick: handleClear, size: "sm", variant: "ghost", children: jsx(Icon, { children: jsx(MdCancel, {}) }) })] }));
6289
+ return (jsx(Flex, { direction: "column", gap: 3, children: jsxs(Flex, { alignItems: "center", gap: "2", width: "auto", minWidth: "300px", children: [jsxs(Combobox.Root, { collection: collection, value: currentValue ? [currentValue] : [], onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, allowCustomValue: true, selectionBehavior: "replace", openOnClick: true, flex: 1, children: [jsxs(Combobox.Control, { children: [jsx(InputGroup$1, { startElement: jsx(BsClock, {}), children: jsx(Combobox.Input, { placeholder: "hh:mm a", onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown }) }), jsx(Combobox.IndicatorGroup, { children: jsx(Combobox.Trigger, {}) })] }), jsx(Portal, { disabled: !portalled, children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [jsx(Combobox.Empty, { children: "No time found" }), collection.items.map((item) => (jsxs(Combobox.Item, { item: item, children: [jsxs(Flex, { alignItems: "center", gap: 2, width: "100%", children: [jsx(Text, { flex: 1, children: item.label }), item.durationText && (jsx(Tag$1.Root, { size: "sm", colorPalette: "blue", children: jsx(Tag$1.Label, { children: item.durationText }) }))] }), jsx(Combobox.ItemIndicator, {})] }, item.value)))] }) }) })] }), durationDiff && (jsx(Tag$1.Root, { size: "sm", children: jsx(Tag$1.Label, { children: durationDiff }) })), jsx(Button$1, { onClick: handleClear, size: "sm", variant: "ghost", children: jsx(Icon, { children: jsx(MdCancel, {}) }) })] }) }));
6039
6290
  }
6040
6291
 
6041
6292
  dayjs.extend(timezone);
6042
6293
  const TimePicker = ({ column, schema, prefix }) => {
6043
6294
  const { watch, formState: { errors }, setValue, } = useFormContext();
6044
- const { translate, timezone, insideDialog } = useSchemaContext();
6295
+ const { timezone, insideDialog } = useSchemaContext();
6045
6296
  const { required, gridColumn = 'span 12', gridRow = 'span 1', timeFormat = 'HH:mm:ssZ', displayTimeFormat = 'hh:mm A', } = schema;
6046
6297
  const isRequired = required?.some((columnId) => columnId === column);
6047
6298
  const colLabel = `${prefix}${column}`;
6299
+ const formI18n = useFormI18n$1(column, prefix, schema);
6048
6300
  const [open, setOpen] = useState(false);
6049
6301
  const value = watch(colLabel);
6050
6302
  const displayedTime = dayjs(`1970-01-01T${value}`).tz(timezone).isValid()
@@ -6099,10 +6351,8 @@ const TimePicker = ({ column, schema, prefix }) => {
6099
6351
  const timeString = getTimeString(newHour, newMinute, newMeridiem);
6100
6352
  setValue(colLabel, timeString, { shouldValidate: true, shouldDirty: true });
6101
6353
  };
6102
- return (jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
6103
- gridRow, errorText: errors[`${colLabel}`]
6104
- ? translate.t(removeIndex(`${colLabel}.field_required`))
6105
- : undefined, invalid: !!errors[colLabel], children: jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
6354
+ return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
6355
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
6106
6356
  setOpen(true);
6107
6357
  }, justifyContent: 'start', children: [jsx(IoMdClock, {}), !!value ? `${displayedTime}` : ''] }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { maxH: "70vh", overflowY: "auto", children: jsx(Popover.Body, { overflow: "visible", children: jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, meridiemLabel: {
6108
6358
  am: translate.t(`common.am`, { defaultValue: 'AM' }),
@@ -6115,21 +6365,148 @@ const TimePicker = ({ column, schema, prefix }) => {
6115
6365
 
6116
6366
  dayjs.extend(utc);
6117
6367
  dayjs.extend(timezone);
6118
- function IsoTimePicker({ hour, setHour, minute, setMinute, second, setSecond,
6119
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
6120
- onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Kong', }) {
6121
- // Generate time options (every 15 minutes, seconds always 0)
6122
- const timeOptions = useMemo(() => {
6123
- const options = [];
6124
- // Get start time for comparison if provided
6125
- let startDateTime = null;
6126
- let shouldFilterByDate = false;
6127
- if (startTime && selectedDate) {
6128
- const startDateObj = dayjs(startTime).tz(timezone);
6129
- const selectedDateObj = dayjs(selectedDate).tz(timezone);
6130
- if (startDateObj.isValid() && selectedDateObj.isValid()) {
6131
- startDateTime = startDateObj;
6132
- // Only filter if dates are the same
6368
+ dayjs.extend(customParseFormat);
6369
+ function DatePickerInput({ value, onChange, placeholder = 'Select a date', dateFormat = 'YYYY-MM-DD', displayFormat = 'YYYY-MM-DD', labels = {
6370
+ monthNamesShort: [
6371
+ 'Jan',
6372
+ 'Feb',
6373
+ 'Mar',
6374
+ 'Apr',
6375
+ 'May',
6376
+ 'Jun',
6377
+ 'Jul',
6378
+ 'Aug',
6379
+ 'Sep',
6380
+ 'Oct',
6381
+ 'Nov',
6382
+ 'Dec',
6383
+ ],
6384
+ weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
6385
+ backButtonLabel: 'Back',
6386
+ forwardButtonLabel: 'Next',
6387
+ }, timezone = 'Asia/Hong_Kong', minDate, maxDate, firstDayOfWeek, showOutsideDays, monthsToDisplay = 1, insideDialog = false, readOnly = false, }) {
6388
+ const [open, setOpen] = useState(false);
6389
+ const [inputValue, setInputValue] = useState('');
6390
+ // Update input value when prop value changes
6391
+ useEffect(() => {
6392
+ if (value) {
6393
+ const formatted = typeof value === 'string'
6394
+ ? dayjs(value).tz(timezone).isValid()
6395
+ ? dayjs(value).tz(timezone).format(displayFormat)
6396
+ : ''
6397
+ : dayjs(value).tz(timezone).format(displayFormat);
6398
+ setInputValue(formatted);
6399
+ }
6400
+ else {
6401
+ setInputValue('');
6402
+ }
6403
+ }, [value, displayFormat, timezone]);
6404
+ // Convert value to Date object for DatePicker
6405
+ const selectedDate = value
6406
+ ? typeof value === 'string'
6407
+ ? dayjs(value).tz(timezone).isValid()
6408
+ ? dayjs(value).tz(timezone).toDate()
6409
+ : new Date()
6410
+ : value
6411
+ : new Date();
6412
+ // Shared function to parse and validate input value
6413
+ const parseAndValidateInput = (inputVal) => {
6414
+ // If empty, clear the value
6415
+ if (!inputVal.trim()) {
6416
+ onChange?.(undefined);
6417
+ setInputValue('');
6418
+ return;
6419
+ }
6420
+ // Try parsing with displayFormat first
6421
+ let parsedDate = dayjs(inputVal, displayFormat, true);
6422
+ // If that fails, try common date formats
6423
+ if (!parsedDate.isValid()) {
6424
+ parsedDate = dayjs(inputVal);
6425
+ }
6426
+ // If still invalid, try parsing with dateFormat
6427
+ if (!parsedDate.isValid()) {
6428
+ parsedDate = dayjs(inputVal, dateFormat, true);
6429
+ }
6430
+ // If valid, check constraints and update
6431
+ if (parsedDate.isValid()) {
6432
+ const dateObj = parsedDate.tz(timezone).toDate();
6433
+ // Check min/max constraints
6434
+ if (minDate && dateObj < minDate) {
6435
+ // Invalid: before minDate, reset to prop value
6436
+ resetToPropValue();
6437
+ return;
6438
+ }
6439
+ if (maxDate && dateObj > maxDate) {
6440
+ // Invalid: after maxDate, reset to prop value
6441
+ resetToPropValue();
6442
+ return;
6443
+ }
6444
+ // Valid date - format and update
6445
+ const formattedDate = parsedDate.tz(timezone).format(dateFormat);
6446
+ const formattedDisplay = parsedDate.tz(timezone).format(displayFormat);
6447
+ onChange?.(formattedDate);
6448
+ setInputValue(formattedDisplay);
6449
+ }
6450
+ else {
6451
+ // Invalid date - reset to prop value
6452
+ resetToPropValue();
6453
+ }
6454
+ };
6455
+ // Helper function to reset input to prop value
6456
+ const resetToPropValue = () => {
6457
+ if (value) {
6458
+ const formatted = typeof value === 'string'
6459
+ ? dayjs(value).tz(timezone).isValid()
6460
+ ? dayjs(value).tz(timezone).format(displayFormat)
6461
+ : ''
6462
+ : dayjs(value).tz(timezone).format(displayFormat);
6463
+ setInputValue(formatted);
6464
+ }
6465
+ else {
6466
+ setInputValue('');
6467
+ }
6468
+ };
6469
+ const handleInputChange = (e) => {
6470
+ // Only update the input value, don't parse yet
6471
+ setInputValue(e.target.value);
6472
+ };
6473
+ const handleInputBlur = () => {
6474
+ // Parse and validate when input loses focus
6475
+ parseAndValidateInput(inputValue);
6476
+ };
6477
+ const handleKeyDown = (e) => {
6478
+ // Parse and validate when Enter is pressed
6479
+ if (e.key === 'Enter') {
6480
+ e.preventDefault();
6481
+ parseAndValidateInput(inputValue);
6482
+ }
6483
+ };
6484
+ const handleDateSelected = ({ date }) => {
6485
+ const formattedDate = dayjs(date).tz(timezone).format(dateFormat);
6486
+ onChange?.(formattedDate);
6487
+ setOpen(false);
6488
+ };
6489
+ const datePickerContent = (jsx(DatePicker$1, { selected: selectedDate, onDateSelected: handleDateSelected, labels: labels, minDate: minDate, maxDate: maxDate, firstDayOfWeek: firstDayOfWeek, showOutsideDays: showOutsideDays, monthsToDisplay: monthsToDisplay }));
6490
+ return (jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsx(InputGroup, { endElement: jsx(Popover.Trigger, { asChild: true, children: jsx(IconButton, { variant: "ghost", size: "2xs", "aria-label": "Open calendar", onClick: () => setOpen(true), children: jsx(Icon, { children: jsx(MdDateRange, {}) }) }) }), children: jsx(Input, { value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onKeyDown: handleKeyDown, placeholder: placeholder, readOnly: readOnly }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minH: "25rem", children: jsx(Popover.Body, { children: datePickerContent }) }) })) : (jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minH: "25rem", children: jsx(Popover.Body, { children: datePickerContent }) }) }) }))] }));
6491
+ }
6492
+
6493
+ dayjs.extend(utc);
6494
+ dayjs.extend(timezone);
6495
+ function IsoTimePicker({ hour, setHour, minute, setMinute, second, setSecond,
6496
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
6497
+ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Kong', portalled = true, }) {
6498
+ // Generate time options (every 15 minutes, seconds always 0)
6499
+ const timeOptions = useMemo(() => {
6500
+ const options = [];
6501
+ // Get start time for comparison if provided
6502
+ let startDateTime = null;
6503
+ let shouldFilterByDate = false;
6504
+ if (startTime && selectedDate) {
6505
+ const startDateObj = dayjs(startTime).tz(timezone);
6506
+ const selectedDateObj = dayjs(selectedDate).tz(timezone);
6507
+ if (startDateObj.isValid() && selectedDateObj.isValid()) {
6508
+ startDateTime = startDateObj;
6509
+ // Only filter if dates are the same
6133
6510
  shouldFilterByDate =
6134
6511
  startDateObj.format('YYYY-MM-DD') ===
6135
6512
  selectedDateObj.format('YYYY-MM-DD');
@@ -6152,8 +6529,8 @@ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Ko
6152
6529
  continue; // Skip this option as it would result in negative duration
6153
6530
  }
6154
6531
  }
6155
- // Calculate and append duration if startTime is provided
6156
- let label = timeDisplay;
6532
+ // Calculate duration if startTime is provided
6533
+ let durationText;
6157
6534
  if (startDateTime && selectedDate) {
6158
6535
  const selectedDateObj = dayjs(selectedDate).tz(timezone);
6159
6536
  const optionDateTime = selectedDateObj
@@ -6178,17 +6555,18 @@ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Ko
6178
6555
  else {
6179
6556
  diffText = `${diffSeconds}s`;
6180
6557
  }
6181
- label = `${timeDisplay} (+${diffText})`;
6558
+ durationText = `+${diffText}`;
6182
6559
  }
6183
6560
  }
6184
6561
  }
6185
6562
  options.push({
6186
- label,
6563
+ label: timeDisplay,
6187
6564
  value: `${h}:${m}:0`,
6188
6565
  hour: h,
6189
6566
  minute: m,
6190
6567
  second: 0,
6191
6568
  searchText: timeDisplay, // Use base time without duration for searching
6569
+ durationText,
6192
6570
  });
6193
6571
  }
6194
6572
  }
@@ -6208,46 +6586,46 @@ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Ko
6208
6586
  }
6209
6587
  return `${hour}:${minute}:${second}`;
6210
6588
  }, [hour, minute, second]);
6211
- // Get display text for combobox
6212
- const displayText = useMemo(() => {
6213
- if (hour === null || minute === null || second === null) {
6214
- return '';
6589
+ // Calculate duration difference
6590
+ const durationDiff = useMemo(() => {
6591
+ if (!startTime ||
6592
+ !selectedDate ||
6593
+ hour === null ||
6594
+ minute === null ||
6595
+ second === null) {
6596
+ return null;
6215
6597
  }
6216
- const timeDisplay = `${hour.toString().padStart(2, '0')}:${minute
6217
- .toString()
6218
- .padStart(2, '0')}:${second.toString().padStart(2, '0')}`;
6219
- // Show duration difference if startTime is provided
6220
- if (startTime && selectedDate) {
6221
- const startDateObj = dayjs(startTime).tz(timezone);
6222
- const selectedDateObj = dayjs(selectedDate).tz(timezone);
6223
- const currentDateTime = selectedDateObj
6224
- .hour(hour)
6225
- .minute(minute)
6226
- .second(second ?? 0)
6227
- .millisecond(0);
6228
- if (startDateObj.isValid() && currentDateTime.isValid()) {
6229
- const diffMs = currentDateTime.diff(startDateObj);
6230
- if (diffMs >= 0) {
6231
- const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
6232
- const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
6233
- const diffSeconds = Math.floor((diffMs % (1000 * 60)) / 1000);
6234
- if (diffHours > 0 || diffMinutes > 0 || diffSeconds > 0) {
6235
- let diffText = '';
6236
- if (diffHours > 0) {
6237
- diffText = `${diffHours}h ${diffMinutes}m`;
6238
- }
6239
- else if (diffMinutes > 0) {
6240
- diffText = `${diffMinutes}m ${diffSeconds}s`;
6241
- }
6242
- else {
6243
- diffText = `${diffSeconds}s`;
6244
- }
6245
- return `${timeDisplay} (+${diffText})`;
6246
- }
6247
- }
6598
+ const startDateObj = dayjs(startTime).tz(timezone);
6599
+ const selectedDateObj = dayjs(selectedDate).tz(timezone);
6600
+ const currentDateTime = selectedDateObj
6601
+ .hour(hour)
6602
+ .minute(minute)
6603
+ .second(second ?? 0)
6604
+ .millisecond(0);
6605
+ if (!startDateObj.isValid() || !currentDateTime.isValid()) {
6606
+ return null;
6607
+ }
6608
+ const diffMs = currentDateTime.diff(startDateObj);
6609
+ if (diffMs < 0) {
6610
+ return null;
6611
+ }
6612
+ const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
6613
+ const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
6614
+ const diffSeconds = Math.floor((diffMs % (1000 * 60)) / 1000);
6615
+ if (diffHours > 0 || diffMinutes > 0 || diffSeconds > 0) {
6616
+ let diffText = '';
6617
+ if (diffHours > 0) {
6618
+ diffText = `${diffHours}h ${diffMinutes}m`;
6619
+ }
6620
+ else if (diffMinutes > 0) {
6621
+ diffText = `${diffMinutes}m ${diffSeconds}s`;
6248
6622
  }
6623
+ else {
6624
+ diffText = `${diffSeconds}s`;
6625
+ }
6626
+ return `+${diffText}`;
6249
6627
  }
6250
- return timeDisplay;
6628
+ return null;
6251
6629
  }, [hour, minute, second, startTime, selectedDate, timezone]);
6252
6630
  const handleClear = () => {
6253
6631
  setHour(null);
@@ -6275,16 +6653,17 @@ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Ko
6275
6653
  });
6276
6654
  }
6277
6655
  };
6278
- const handleInputValueChange = (details) => {
6279
- const inputValue = details.inputValue.trim();
6656
+ // Parse input value and update state
6657
+ const parseAndCommitInput = (value) => {
6658
+ const trimmedValue = value.trim();
6280
6659
  // Filter the collection based on input
6281
- filter(inputValue);
6282
- if (!inputValue) {
6660
+ filter(trimmedValue);
6661
+ if (!trimmedValue) {
6283
6662
  return;
6284
6663
  }
6285
6664
  // Parse HH:mm:ss or HH:mm format
6286
6665
  const timePattern = /^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/;
6287
- const match = inputValue.match(timePattern);
6666
+ const match = trimmedValue.match(timePattern);
6288
6667
  if (match) {
6289
6668
  const parsedHour = parseInt(match[1], 10);
6290
6669
  const parsedMinute = parseInt(match[2], 10);
@@ -6304,11 +6683,12 @@ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Ko
6304
6683
  minute: parsedMinute,
6305
6684
  second: parsedSecond,
6306
6685
  });
6686
+ return;
6307
6687
  }
6308
6688
  }
6309
6689
  else {
6310
6690
  // Try to parse formats like "123045" (HHmmss) or "1230" (HHmm)
6311
- const numbersOnly = inputValue.replace(/[^0-9]/g, '');
6691
+ const numbersOnly = trimmedValue.replace(/[^0-9]/g, '');
6312
6692
  if (numbersOnly.length >= 4) {
6313
6693
  const parsedHour = parseInt(numbersOnly.slice(0, 2), 10);
6314
6694
  const parsedMinute = parseInt(numbersOnly.slice(2, 4), 10);
@@ -6328,11 +6708,56 @@ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Ko
6328
6708
  minute: parsedMinute,
6329
6709
  second: parsedSecond,
6330
6710
  });
6711
+ return;
6331
6712
  }
6332
6713
  }
6333
6714
  }
6715
+ // Parse failed, select first result
6716
+ selectFirstResult();
6717
+ };
6718
+ // Select first result from filtered collection
6719
+ const selectFirstResult = () => {
6720
+ if (collection.items.length > 0) {
6721
+ const firstItem = collection.items[0];
6722
+ setHour(firstItem.hour);
6723
+ setMinute(firstItem.minute);
6724
+ setSecond(firstItem.second);
6725
+ filter(''); // Reset filter after selection
6726
+ onChange({
6727
+ hour: firstItem.hour,
6728
+ minute: firstItem.minute,
6729
+ second: firstItem.second,
6730
+ });
6731
+ }
6732
+ };
6733
+ const handleInputValueChange = (details) => {
6734
+ // Filter the collection based on input, but don't parse yet
6735
+ filter(details.inputValue);
6736
+ };
6737
+ const handleFocus = (e) => {
6738
+ // Select all text when focusing
6739
+ e.target.select();
6740
+ };
6741
+ const handleBlur = (e) => {
6742
+ // Parse and commit the input value when losing focus
6743
+ const inputValue = e.target.value;
6744
+ if (inputValue) {
6745
+ parseAndCommitInput(inputValue);
6746
+ }
6747
+ };
6748
+ const handleKeyDown = (e) => {
6749
+ // Commit input on Enter key
6750
+ if (e.key === 'Enter') {
6751
+ e.preventDefault();
6752
+ const inputValue = e.currentTarget.value;
6753
+ if (inputValue) {
6754
+ parseAndCommitInput(inputValue);
6755
+ }
6756
+ // Blur the input
6757
+ e.currentTarget?.blur();
6758
+ }
6334
6759
  };
6335
- return (jsx(Flex, { direction: "column", gap: 3, children: jsxs(Grid, { justifyContent: 'center', alignItems: 'center', templateColumns: '1fr auto', gap: "2", width: "auto", minWidth: "300px", children: [jsxs(Combobox.Root, { collection: collection, value: currentValue ? [currentValue] : [], onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, allowCustomValue: true, selectionBehavior: "replace", openOnClick: true, width: "100%", children: [jsxs(Combobox.Control, { children: [jsx(InputGroup$1, { startElement: jsx(BsClock, {}), children: jsx(Combobox.Input, { placeholder: "HH:mm:ss", value: displayText }) }), jsxs(Combobox.IndicatorGroup, { children: [jsx(Combobox.ClearTrigger, {}), jsx(Combobox.Trigger, {})] })] }), jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [jsx(Combobox.Empty, { children: "No time found" }), collection.items.map((item) => (jsxs(Combobox.Item, { item: item, children: [item.label, jsx(Combobox.ItemIndicator, {})] }, item.value)))] }) }) })] }), jsx(Button$1, { onClick: handleClear, size: "sm", variant: "ghost", children: jsx(Icon, { children: jsx(MdCancel, {}) }) })] }) }));
6760
+ return (jsx(Flex, { direction: "column", gap: 3, children: jsxs(Flex, { alignItems: "center", gap: "2", width: "auto", minWidth: "300px", children: [jsxs(Combobox.Root, { collection: collection, value: currentValue ? [currentValue] : [], onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, allowCustomValue: true, selectionBehavior: "replace", openOnClick: true, flex: 1, children: [jsxs(Combobox.Control, { children: [jsx(InputGroup$1, { startElement: jsx(BsClock, {}), children: jsx(Combobox.Input, { placeholder: "HH:mm:ss", onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown }) }), jsx(Combobox.IndicatorGroup, { children: jsx(Combobox.Trigger, {}) })] }), jsx(Portal, { disabled: !portalled, children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [jsx(Combobox.Empty, { children: "No time found" }), collection.items.map((item) => (jsxs(Combobox.Item, { item: item, children: [jsxs(Flex, { alignItems: "center", gap: 2, width: "100%", children: [jsx(Text, { flex: 1, children: item.label }), item.durationText && (jsx(Tag$1.Root, { size: "sm", children: jsx(Tag$1.Label, { children: item.durationText }) }))] }), jsx(Combobox.ItemIndicator, {})] }, item.value)))] }) }) })] }), durationDiff && (jsx(Tag$1.Root, { size: "sm", children: jsx(Tag$1.Label, { children: durationDiff }) })), jsx(Button$1, { onClick: handleClear, size: "sm", variant: "ghost", children: jsx(Icon, { children: jsx(MdCancel, {}) }) })] }) }));
6336
6761
  }
6337
6762
 
6338
6763
  dayjs.extend(utc);
@@ -6355,30 +6780,193 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
6355
6780
  weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
6356
6781
  backButtonLabel: 'Back',
6357
6782
  forwardButtonLabel: 'Next',
6358
- }, timezone = 'Asia/Hong_Kong', startTime, }) {
6359
- const [selectedDate, setSelectedDate] = useState(value || '');
6783
+ }, timezone = 'Asia/Hong_Kong', startTime, minDate, maxDate, portalled = false, }) {
6784
+ console.log('[DateTimePicker] Component initialized with props:', {
6785
+ value,
6786
+ format,
6787
+ showSeconds,
6788
+ timezone,
6789
+ startTime,
6790
+ minDate,
6791
+ maxDate,
6792
+ });
6793
+ // Initialize selectedDate from value prop, converting ISO to YYYY-MM-DD format
6794
+ const getDateString = useCallback((val) => {
6795
+ if (!val)
6796
+ return '';
6797
+ const dateObj = dayjs(val).tz(timezone);
6798
+ return dateObj.isValid() ? dateObj.format('YYYY-MM-DD') : '';
6799
+ }, [timezone]);
6800
+ const [selectedDate, setSelectedDate] = useState(getDateString(value));
6801
+ // Helper to get time values from value prop with timezone
6802
+ const getTimeFromValue = useCallback((val) => {
6803
+ console.log('[DateTimePicker] getTimeFromValue called:', {
6804
+ val,
6805
+ timezone,
6806
+ showSeconds,
6807
+ });
6808
+ if (!val) {
6809
+ console.log('[DateTimePicker] No value provided, returning nulls');
6810
+ return {
6811
+ hour12: null,
6812
+ minute: null,
6813
+ meridiem: null,
6814
+ hour24: null,
6815
+ second: null,
6816
+ };
6817
+ }
6818
+ const dateObj = dayjs(val).tz(timezone);
6819
+ console.log('[DateTimePicker] Parsed date object:', {
6820
+ original: val,
6821
+ timezone,
6822
+ isValid: dateObj.isValid(),
6823
+ formatted: dateObj.format('YYYY-MM-DD HH:mm:ss Z'),
6824
+ hour24: dateObj.hour(),
6825
+ minute: dateObj.minute(),
6826
+ second: dateObj.second(),
6827
+ });
6828
+ if (!dateObj.isValid()) {
6829
+ console.log('[DateTimePicker] Invalid date object, returning nulls');
6830
+ return {
6831
+ hour12: null,
6832
+ minute: null,
6833
+ meridiem: null,
6834
+ hour24: null,
6835
+ second: null,
6836
+ };
6837
+ }
6838
+ const hour24Value = dateObj.hour();
6839
+ const hour12Value = hour24Value % 12 || 12;
6840
+ const minuteValue = dateObj.minute();
6841
+ const meridiemValue = hour24Value >= 12 ? 'pm' : 'am';
6842
+ const secondValue = showSeconds ? dateObj.second() : null;
6843
+ const result = {
6844
+ hour12: hour12Value,
6845
+ minute: minuteValue,
6846
+ meridiem: meridiemValue,
6847
+ hour24: hour24Value,
6848
+ second: secondValue,
6849
+ };
6850
+ console.log('[DateTimePicker] Extracted time values:', result);
6851
+ return result;
6852
+ }, [timezone, showSeconds]);
6853
+ const initialTime = getTimeFromValue(value);
6854
+ console.log('[DateTimePicker] Initial time from value:', {
6855
+ value,
6856
+ initialTime,
6857
+ });
6360
6858
  // Time state for 12-hour format
6361
- const [hour12, setHour12] = useState(value ? dayjs(value).hour() % 12 || 12 : null);
6362
- const [minute, setMinute] = useState(value ? dayjs(value).minute() : null);
6363
- const [meridiem, setMeridiem] = useState(value ? (dayjs(value).hour() >= 12 ? 'pm' : 'am') : null);
6859
+ const [hour12, setHour12] = useState(initialTime.hour12);
6860
+ const [minute, setMinute] = useState(initialTime.minute);
6861
+ const [meridiem, setMeridiem] = useState(initialTime.meridiem);
6364
6862
  // Time state for 24-hour format
6365
- const [hour24, setHour24] = useState(value ? dayjs(value).hour() : null);
6366
- const [second, setSecond] = useState(showSeconds && value ? dayjs(value).second() : null);
6863
+ const [hour24, setHour24] = useState(initialTime.hour24);
6864
+ const [second, setSecond] = useState(initialTime.second);
6865
+ // Sync selectedDate and time states when value prop changes
6866
+ useEffect(() => {
6867
+ console.log('[DateTimePicker] useEffect triggered - value changed:', {
6868
+ value,
6869
+ timezone,
6870
+ format,
6871
+ });
6872
+ // If value is null, undefined, or invalid, clear all fields
6873
+ if (!value || value === null || value === undefined) {
6874
+ console.log('[DateTimePicker] Value is null/undefined, clearing all fields');
6875
+ setSelectedDate('');
6876
+ setHour12(null);
6877
+ setMinute(null);
6878
+ setMeridiem(null);
6879
+ setHour24(null);
6880
+ setSecond(null);
6881
+ return;
6882
+ }
6883
+ // Check if value is valid
6884
+ const dateObj = dayjs(value).tz(timezone);
6885
+ if (!dateObj.isValid()) {
6886
+ console.log('[DateTimePicker] Invalid value, clearing all fields');
6887
+ setSelectedDate('');
6888
+ setHour12(null);
6889
+ setMinute(null);
6890
+ setMeridiem(null);
6891
+ setHour24(null);
6892
+ setSecond(null);
6893
+ return;
6894
+ }
6895
+ const dateString = getDateString(value);
6896
+ console.log('[DateTimePicker] Setting selectedDate:', dateString);
6897
+ setSelectedDate(dateString);
6898
+ const timeData = getTimeFromValue(value);
6899
+ console.log('[DateTimePicker] Updating time states:', {
6900
+ timeData,
6901
+ });
6902
+ setHour12(timeData.hour12);
6903
+ setMinute(timeData.minute);
6904
+ setMeridiem(timeData.meridiem);
6905
+ setHour24(timeData.hour24);
6906
+ setSecond(timeData.second);
6907
+ }, [value, getTimeFromValue, getDateString, timezone]);
6367
6908
  const handleDateChange = (date) => {
6909
+ console.log('[DateTimePicker] handleDateChange called:', {
6910
+ date,
6911
+ timezone,
6912
+ showSeconds,
6913
+ currentTimeStates: { hour12, minute, meridiem, hour24, second },
6914
+ });
6915
+ // If date is empty or invalid, clear all fields
6916
+ if (!date || date === '') {
6917
+ console.log('[DateTimePicker] Empty date, clearing all fields');
6918
+ setSelectedDate('');
6919
+ setHour12(null);
6920
+ setMinute(null);
6921
+ setMeridiem(null);
6922
+ setHour24(null);
6923
+ setSecond(null);
6924
+ onChange?.(undefined);
6925
+ return;
6926
+ }
6368
6927
  setSelectedDate(date);
6928
+ // Parse the date string (YYYY-MM-DD) in the specified timezone
6929
+ const dateObj = dayjs.tz(date, timezone);
6930
+ console.log('[DateTimePicker] Parsed date object:', {
6931
+ date,
6932
+ timezone,
6933
+ isValid: dateObj.isValid(),
6934
+ isoString: dateObj.toISOString(),
6935
+ formatted: dateObj.format('YYYY-MM-DD HH:mm:ss Z'),
6936
+ });
6937
+ if (!dateObj.isValid()) {
6938
+ console.warn('[DateTimePicker] Invalid date object in handleDateChange, clearing fields');
6939
+ setSelectedDate('');
6940
+ setHour12(null);
6941
+ setMinute(null);
6942
+ setMeridiem(null);
6943
+ setHour24(null);
6944
+ setSecond(null);
6945
+ onChange?.(undefined);
6946
+ return;
6947
+ }
6369
6948
  // When showSeconds is false, ignore seconds from the date
6370
- const dateObj = dayjs(date).tz(timezone);
6371
- if (!showSeconds && dateObj.isValid()) {
6949
+ if (!showSeconds) {
6372
6950
  const dateWithoutSeconds = dateObj.second(0).millisecond(0).toISOString();
6951
+ console.log('[DateTimePicker] Updating date without seconds:', dateWithoutSeconds);
6373
6952
  updateDateTime(dateWithoutSeconds);
6374
6953
  }
6375
6954
  else {
6376
- updateDateTime(dateObj.toISOString());
6955
+ const dateWithSeconds = dateObj.toISOString();
6956
+ console.log('[DateTimePicker] Updating date with seconds:', dateWithSeconds);
6957
+ updateDateTime(dateWithSeconds);
6377
6958
  }
6378
6959
  };
6379
6960
  const handleTimeChange = (timeData) => {
6961
+ console.log('[DateTimePicker] handleTimeChange called:', {
6962
+ timeData,
6963
+ format,
6964
+ selectedDate,
6965
+ timezone,
6966
+ });
6380
6967
  if (format === 'iso-date-time') {
6381
6968
  const data = timeData;
6969
+ console.log('[DateTimePicker] ISO format - setting 24-hour time:', data);
6382
6970
  setHour24(data.hour);
6383
6971
  setMinute(data.minute);
6384
6972
  if (showSeconds) {
@@ -6391,60 +6979,161 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
6391
6979
  }
6392
6980
  else {
6393
6981
  const data = timeData;
6982
+ console.log('[DateTimePicker] 12-hour format - setting time:', data);
6394
6983
  setHour12(data.hour);
6395
6984
  setMinute(data.minute);
6396
6985
  setMeridiem(data.meridiem);
6397
6986
  }
6398
- // Use selectedDate if valid, otherwise use today's date as fallback
6399
- const dateToUse = selectedDate && dayjs(selectedDate).isValid()
6400
- ? selectedDate
6401
- : dayjs().tz(timezone).toISOString();
6402
- const dateObj = dayjs(dateToUse).tz(timezone);
6987
+ // Use selectedDate if valid, otherwise clear all fields
6988
+ if (!selectedDate || !dayjs(selectedDate).isValid()) {
6989
+ console.log('[DateTimePicker] No valid selectedDate, clearing all fields');
6990
+ setSelectedDate('');
6991
+ setHour12(null);
6992
+ setMinute(null);
6993
+ setMeridiem(null);
6994
+ setHour24(null);
6995
+ setSecond(null);
6996
+ onChange?.(undefined);
6997
+ return;
6998
+ }
6999
+ const dateObj = dayjs(selectedDate).tz(timezone);
6403
7000
  if (dateObj.isValid()) {
6404
7001
  updateDateTime(dateObj.toISOString(), timeData);
6405
7002
  }
7003
+ else {
7004
+ console.warn('[DateTimePicker] Invalid date object in handleTimeChange, clearing fields');
7005
+ setSelectedDate('');
7006
+ setHour12(null);
7007
+ setMinute(null);
7008
+ setMeridiem(null);
7009
+ setHour24(null);
7010
+ setSecond(null);
7011
+ onChange?.(undefined);
7012
+ }
6406
7013
  };
6407
7014
  const updateDateTime = (date, timeData) => {
6408
- if (!date) {
7015
+ console.log('[DateTimePicker] updateDateTime called:', {
7016
+ date,
7017
+ timeData,
7018
+ format,
7019
+ currentStates: { hour12, minute, meridiem, hour24, second },
7020
+ });
7021
+ if (!date || date === null || date === undefined) {
7022
+ console.log('[DateTimePicker] No date provided, clearing all fields and calling onChange(undefined)');
7023
+ setSelectedDate('');
7024
+ setHour12(null);
7025
+ setMinute(null);
7026
+ setMeridiem(null);
7027
+ setHour24(null);
7028
+ setSecond(null);
6409
7029
  onChange?.(undefined);
6410
7030
  return;
6411
7031
  }
6412
7032
  // use dayjs to convert the date to the timezone
6413
7033
  const dateObj = dayjs(date).tz(timezone);
6414
7034
  if (!dateObj.isValid()) {
7035
+ console.warn('[DateTimePicker] Invalid date object in updateDateTime, clearing fields:', date);
7036
+ setSelectedDate('');
7037
+ setHour12(null);
7038
+ setMinute(null);
7039
+ setMeridiem(null);
7040
+ setHour24(null);
7041
+ setSecond(null);
7042
+ onChange?.(undefined);
6415
7043
  return;
6416
7044
  }
6417
7045
  const newDate = dateObj.toDate();
6418
7046
  if (format === 'iso-date-time') {
6419
7047
  const data = timeData;
6420
- const h = data?.hour ?? hour24;
6421
- const m = data?.minute ?? minute;
7048
+ // Use timeData values if provided, otherwise fall back to current state
7049
+ // But if timeData is explicitly provided with nulls, we need to check if all are null
7050
+ const h = data !== undefined ? data.hour : hour24;
7051
+ const m = data !== undefined ? data.minute : minute;
6422
7052
  // Always ignore seconds when showSeconds is false - set to 0
6423
- const s = showSeconds ? data?.second ?? second ?? 0 : 0;
7053
+ const s = showSeconds
7054
+ ? data !== undefined
7055
+ ? data.second ?? null
7056
+ : second ?? 0
7057
+ : 0;
7058
+ // If all time values are null, clear the value
7059
+ if (h === null && m === null && (showSeconds ? s === null : true)) {
7060
+ console.log('[DateTimePicker] All time values are null, clearing value');
7061
+ onChange?.(undefined);
7062
+ return;
7063
+ }
7064
+ console.log('[DateTimePicker] ISO format - setting time on date:', {
7065
+ h,
7066
+ m,
7067
+ s,
7068
+ showSeconds,
7069
+ });
6424
7070
  if (h !== null)
6425
7071
  newDate.setHours(h);
6426
7072
  if (m !== null)
6427
7073
  newDate.setMinutes(m);
6428
- newDate.setSeconds(s);
7074
+ newDate.setSeconds(s ?? 0);
6429
7075
  }
6430
7076
  else {
6431
7077
  const data = timeData;
6432
- const h = data?.hour ?? hour12;
6433
- const m = data?.minute ?? minute;
6434
- const mer = data?.meridiem ?? meridiem;
7078
+ console.log('[DateTimePicker] Processing 12-hour format:', {
7079
+ 'data !== undefined': data !== undefined,
7080
+ 'data?.hour': data?.hour,
7081
+ 'data?.minute': data?.minute,
7082
+ 'data?.meridiem': data?.meridiem,
7083
+ 'current hour12': hour12,
7084
+ 'current minute': minute,
7085
+ 'current meridiem': meridiem,
7086
+ });
7087
+ // Use timeData values if provided, otherwise fall back to current state
7088
+ const h = data !== undefined ? data.hour : hour12;
7089
+ const m = data !== undefined ? data.minute : minute;
7090
+ const mer = data !== undefined ? data.meridiem : meridiem;
7091
+ console.log('[DateTimePicker] Resolved time values:', { h, m, mer });
7092
+ // If all time values are null, clear the value
7093
+ if (h === null && m === null && mer === null) {
7094
+ console.log('[DateTimePicker] All time values are null, clearing value');
7095
+ onChange?.(undefined);
7096
+ return;
7097
+ }
7098
+ console.log('[DateTimePicker] 12-hour format - converting time:', {
7099
+ h,
7100
+ m,
7101
+ mer,
7102
+ });
6435
7103
  if (h !== null && mer !== null) {
6436
7104
  let hour24 = h;
6437
7105
  if (mer === 'am' && h === 12)
6438
7106
  hour24 = 0;
6439
7107
  else if (mer === 'pm' && h < 12)
6440
7108
  hour24 = h + 12;
7109
+ console.log('[DateTimePicker] Converted to 24-hour:', {
7110
+ h,
7111
+ mer,
7112
+ hour24,
7113
+ });
6441
7114
  newDate.setHours(hour24);
6442
7115
  }
6443
- if (m !== null)
7116
+ else {
7117
+ console.log('[DateTimePicker] Skipping hour update - h or mer is null:', {
7118
+ h,
7119
+ mer,
7120
+ });
7121
+ }
7122
+ if (m !== null) {
6444
7123
  newDate.setMinutes(m);
7124
+ }
7125
+ else {
7126
+ console.log('[DateTimePicker] Skipping minute update - m is null');
7127
+ }
6445
7128
  newDate.setSeconds(0);
6446
7129
  }
6447
- onChange?.(dayjs(newDate).tz(timezone).toISOString());
7130
+ const finalISO = dayjs(newDate).tz(timezone).toISOString();
7131
+ console.log('[DateTimePicker] Final ISO string to emit:', {
7132
+ newDate: newDate.toISOString(),
7133
+ timezone,
7134
+ finalISO,
7135
+ });
7136
+ onChange?.(finalISO);
6448
7137
  };
6449
7138
  const handleClear = () => {
6450
7139
  setSelectedDate('');
@@ -6460,14 +7149,92 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
6460
7149
  const normalizedStartTime = startTime
6461
7150
  ? dayjs(startTime).tz(timezone).millisecond(0).toISOString()
6462
7151
  : undefined;
6463
- return (jsxs(Flex, { direction: "column", gap: 4, p: 4, border: "1px solid", borderColor: "gray.200", borderRadius: "md", children: [jsx(DatePicker$1, { selected: selectedDate ? dayjs(selectedDate).tz(timezone).toDate() : new Date(), onDateSelected: ({ date }) => handleDateChange(dayjs(date).tz(timezone).toISOString()), monthsToDisplay: 1, labels: labels, minDate: normalizedStartTime &&
6464
- dayjs(normalizedStartTime).tz(timezone).isValid()
6465
- ? dayjs(normalizedStartTime).tz(timezone).startOf('day').toDate()
6466
- : undefined }), jsxs(Grid, { templateColumns: "1fr auto", alignItems: "center", gap: 4, children: [isISO ? (jsx(IsoTimePicker, { hour: hour24, setHour: setHour24, minute: minute, setMinute: setMinute, second: showSeconds ? second : null, setSecond: showSeconds ? setSecond : () => { }, onChange: handleTimeChange, startTime: normalizedStartTime, selectedDate: selectedDate, timezone: timezone })) : (jsx(TimePicker$1, { hour: hour12, setHour: setHour12, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, startTime: normalizedStartTime, selectedDate: selectedDate, timezone: timezone })), jsx(Button$1, { onClick: handleClear, size: "sm", variant: "outline", colorScheme: "red", children: jsx(Icon, { as: FaTrash }) })] }), selectedDate && (jsxs(Flex, { gap: 2, children: [jsx(Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: dayjs(value).format(isISO
6467
- ? showSeconds
6468
- ? 'YYYY-MM-DD HH:mm:ss'
6469
- : 'YYYY-MM-DD HH:mm'
6470
- : 'YYYY-MM-DD hh:mm A ') }), jsx(Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: dayjs(value).tz(timezone).format('Z') }), jsx(Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: timezone })] }))] }));
7152
+ // Determine minDate: prioritize explicit minDate prop, then fall back to startTime
7153
+ const effectiveMinDate = minDate
7154
+ ? minDate
7155
+ : normalizedStartTime && dayjs(normalizedStartTime).tz(timezone).isValid()
7156
+ ? dayjs(normalizedStartTime).tz(timezone).startOf('day').toDate()
7157
+ : undefined;
7158
+ // Log current state before render
7159
+ useEffect(() => {
7160
+ console.log('[DateTimePicker] Current state before render:', {
7161
+ isISO,
7162
+ hour12,
7163
+ minute,
7164
+ meridiem,
7165
+ hour24,
7166
+ second,
7167
+ selectedDate,
7168
+ normalizedStartTime,
7169
+ timezone,
7170
+ });
7171
+ }, [
7172
+ isISO,
7173
+ hour12,
7174
+ minute,
7175
+ meridiem,
7176
+ hour24,
7177
+ second,
7178
+ selectedDate,
7179
+ normalizedStartTime,
7180
+ timezone,
7181
+ ]);
7182
+ // Compute display text from current state
7183
+ const displayText = useMemo(() => {
7184
+ if (!selectedDate)
7185
+ return null;
7186
+ const dateObj = dayjs.tz(selectedDate, timezone);
7187
+ if (!dateObj.isValid())
7188
+ return null;
7189
+ if (isISO) {
7190
+ // For ISO format, use hour24, minute, second
7191
+ if (hour24 === null || minute === null)
7192
+ return null;
7193
+ const dateTimeObj = dateObj
7194
+ .hour(hour24)
7195
+ .minute(minute)
7196
+ .second(second ?? 0);
7197
+ return dateTimeObj.format(showSeconds ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm');
7198
+ }
7199
+ else {
7200
+ // For 12-hour format, use hour12, minute, meridiem
7201
+ if (hour12 === null || minute === null || meridiem === null)
7202
+ return null;
7203
+ // Convert to 24-hour format for dayjs
7204
+ let hour24Value = hour12;
7205
+ if (meridiem === 'am' && hour12 === 12)
7206
+ hour24Value = 0;
7207
+ else if (meridiem === 'pm' && hour12 < 12)
7208
+ hour24Value = hour12 + 12;
7209
+ const dateTimeObj = dateObj.hour(hour24Value).minute(minute).second(0);
7210
+ return dateTimeObj.format('YYYY-MM-DD hh:mm A');
7211
+ }
7212
+ }, [
7213
+ selectedDate,
7214
+ isISO,
7215
+ hour12,
7216
+ minute,
7217
+ meridiem,
7218
+ hour24,
7219
+ second,
7220
+ showSeconds,
7221
+ timezone,
7222
+ ]);
7223
+ const timezoneOffset = useMemo(() => {
7224
+ if (!selectedDate)
7225
+ return null;
7226
+ const dateObj = dayjs.tz(selectedDate, timezone);
7227
+ return dateObj.isValid() ? dateObj.format('Z') : null;
7228
+ }, [selectedDate, timezone]);
7229
+ return (jsxs(Flex, { direction: "column", gap: 4, p: 4, border: "1px solid", borderColor: "gray.200", borderRadius: "md", children: [jsx(DatePickerInput, { value: selectedDate || undefined, onChange: (date) => {
7230
+ if (date) {
7231
+ handleDateChange(date);
7232
+ }
7233
+ else {
7234
+ setSelectedDate('');
7235
+ onChange?.(undefined);
7236
+ }
7237
+ }, placeholder: "Select a date", dateFormat: "YYYY-MM-DD", displayFormat: "YYYY-MM-DD", labels: labels, timezone: timezone, minDate: effectiveMinDate, maxDate: maxDate, monthsToDisplay: 1, readOnly: true }), jsxs(Grid, { templateColumns: "1fr auto", alignItems: "center", gap: 4, children: [isISO ? (jsx(IsoTimePicker, { hour: hour24, setHour: setHour24, minute: minute, setMinute: setMinute, second: showSeconds ? second : null, setSecond: showSeconds ? setSecond : () => { }, onChange: handleTimeChange, startTime: normalizedStartTime, selectedDate: selectedDate, timezone: timezone, portalled: portalled })) : (jsx(TimePicker$1, { hour: hour12, setHour: setHour12, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, startTime: normalizedStartTime, selectedDate: selectedDate, timezone: timezone, portalled: portalled })), jsx(Button$1, { onClick: handleClear, size: "sm", variant: "outline", colorScheme: "red", children: jsx(Icon, { as: FaTrash }) })] }), displayText && (jsxs(Flex, { gap: 2, children: [jsx(Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: displayText }), timezoneOffset && (jsx(Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: timezoneOffset })), jsx(Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: timezone })] }))] }));
6471
7238
  }
6472
7239
 
6473
7240
  dayjs.extend(utc);
@@ -6475,7 +7242,7 @@ dayjs.extend(timezone);
6475
7242
  const DateTimePicker = ({ column, schema, prefix, }) => {
6476
7243
  const { watch, formState: { errors }, setValue, } = useFormContext();
6477
7244
  const { timezone, dateTimePickerLabels, insideDialog } = useSchemaContext();
6478
- const formI18n = useFormI18n(column, prefix);
7245
+ const formI18n = useFormI18n$1(column, prefix, schema);
6479
7246
  const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD HH:mm:ss',
6480
7247
  // with timezone
6481
7248
  dateFormat = 'YYYY-MM-DD[T]HH:mm:ssZ', } = schema;
@@ -6483,32 +7250,9 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
6483
7250
  const colLabel = formI18n.colLabel;
6484
7251
  const [open, setOpen] = useState(false);
6485
7252
  const selectedDate = watch(colLabel);
6486
- const displayDate = dayjs(selectedDate)
6487
- .tz(timezone)
6488
- .format(displayDateFormat);
6489
- useEffect(() => {
6490
- try {
6491
- if (selectedDate) {
6492
- // Parse the selectedDate as UTC or in a specific timezone to avoid +8 hour shift
6493
- // For example, parse as UTC:
6494
- const parsedDate = dayjs(selectedDate).tz(timezone);
6495
- if (!parsedDate.isValid())
6496
- return;
6497
- // Format according to dateFormat from schema
6498
- const formatted = parsedDate.format(dateFormat);
6499
- // Update the form value only if different to avoid loops
6500
- if (formatted !== selectedDate) {
6501
- setValue(colLabel, formatted, {
6502
- shouldValidate: true,
6503
- shouldDirty: true,
6504
- });
6505
- }
6506
- }
6507
- }
6508
- catch (e) {
6509
- console.error(e);
6510
- }
6511
- }, [selectedDate, dateFormat, colLabel, setValue]);
7253
+ const displayDate = selectedDate && dayjs(selectedDate).tz(timezone).isValid()
7254
+ ? dayjs(selectedDate).tz(timezone).format(displayDateFormat)
7255
+ : '';
6512
7256
  const dateTimePickerLabelsConfig = {
6513
7257
  monthNamesShort: dateTimePickerLabels?.monthNamesShort ?? [
6514
7258
  formI18n.translate.t(`common.month_1`, {
@@ -6581,12 +7325,22 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
6581
7325
  }),
6582
7326
  };
6583
7327
  const dateTimePickerContent = (jsx(DateTimePicker$1, { value: selectedDate, onChange: (date) => {
6584
- setValue(colLabel, dayjs(date).tz(timezone).format(dateFormat));
7328
+ if (!date || date === null || date === undefined) {
7329
+ setValue(colLabel, undefined);
7330
+ return;
7331
+ }
7332
+ const dateObj = dayjs(date).tz(timezone);
7333
+ if (dateObj.isValid()) {
7334
+ setValue(colLabel, dateObj.format(dateFormat));
7335
+ }
7336
+ else {
7337
+ setValue(colLabel, undefined);
7338
+ }
6585
7339
  }, timezone: timezone, labels: dateTimePickerLabelsConfig }));
6586
7340
  return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
6587
- gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
7341
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
6588
7342
  setOpen(true);
6589
- }, justifyContent: 'start', children: [jsx(MdDateRange, {}), selectedDate !== undefined ? `${displayDate}` : ''] }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minW: "450px", minH: "25rem", children: jsx(Popover.Body, { children: dateTimePickerContent }) }) })) : (jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minW: "450px", minH: "25rem", children: jsx(Popover.Body, { children: dateTimePickerContent }) }) }) }))] }) }));
7343
+ }, justifyContent: 'start', children: [jsx(MdDateRange, {}), displayDate || ''] }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minW: "450px", minH: "25rem", children: jsx(Popover.Body, { children: dateTimePickerContent }) }) })) : (jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minW: "450px", minH: "25rem", children: jsx(Popover.Body, { children: dateTimePickerContent }) }) }) }))] }) }));
6590
7344
  };
6591
7345
 
6592
7346
  const SchemaRenderer = ({ schema, prefix, column, }) => {
@@ -6630,7 +7384,7 @@ const SchemaRenderer = ({ schema, prefix, column, }) => {
6630
7384
  if (innerProperties) {
6631
7385
  return jsx(ObjectInput, { schema: colSchema, prefix, column });
6632
7386
  }
6633
- return jsx(RecordInput$1, { schema: colSchema, prefix, column });
7387
+ return jsx(RecordInput, { schema: colSchema, prefix, column });
6634
7388
  }
6635
7389
  if (type === 'array') {
6636
7390
  if (variant === 'id-picker') {
@@ -6684,32 +7438,30 @@ const ColumnRenderer = ({ column, properties, prefix, parentRequired, }) => {
6684
7438
  };
6685
7439
 
6686
7440
  const ArrayViewer = ({ schema, column, prefix }) => {
6687
- const { gridColumn = "span 12", gridRow = "span 1", required, items, } = schema;
6688
- const { translate } = useSchemaContext();
7441
+ const { gridColumn = 'span 12', gridRow = 'span 1', required, items, } = schema;
6689
7442
  const colLabel = `${prefix}${column}`;
6690
7443
  const isRequired = required?.some((columnId) => columnId === column);
7444
+ const formI18n = useFormI18n$1(column, prefix, schema);
6691
7445
  const { watch, formState: { errors }, } = useFormContext();
6692
7446
  const values = watch(colLabel) ?? [];
6693
- return (jsxs(Box, { gridRow, gridColumn, children: [jsxs(Box, { as: "label", gridColumn: "1/span12", children: [`${translate.t(removeIndex(`${colLabel}.field_label`))}`, isRequired && jsx("span", { children: "*" })] }), jsx(Flex, { flexFlow: "column", gap: 1, children: values.map((field, index) => (jsx(Flex, { flexFlow: "column", bgColor: { base: "colorPalette.100", _dark: "colorPalette.900" }, p: "2", borderRadius: "md", borderWidth: "thin", borderColor: {
6694
- base: "colorPalette.200",
6695
- _dark: "colorPalette.800",
6696
- }, children: jsx(Grid, { gap: "4", gridTemplateColumns: "repeat(12, 1fr)", autoFlow: "row", children: jsx(SchemaViewer, { column: `${index}`,
7447
+ return (jsxs(Box, { gridRow, gridColumn, children: [jsxs(Box, { as: "label", gridColumn: '1/span12', children: [formI18n.label(), isRequired && jsx("span", { children: "*" })] }), jsx(Flex, { flexFlow: 'column', gap: 1, children: values.map((field, index) => (jsx(Flex, { flexFlow: 'column', bgColor: { base: 'colorPalette.100', _dark: 'colorPalette.900' }, p: '2', borderRadius: 'md', borderWidth: 'thin', borderColor: {
7448
+ base: 'colorPalette.200',
7449
+ _dark: 'colorPalette.800',
7450
+ }, children: jsx(Grid, { gap: "4", gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: jsx(SchemaViewer, { column: `${index}`,
6697
7451
  prefix: `${colLabel}.`,
6698
7452
  // @ts-expect-error find suitable types
6699
- schema: { showLabel: false, ...(items ?? {}) } }) }) }, `form-${prefix}${column}.${index}`))) }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
7453
+ schema: { showLabel: false, ...(items ?? {}) } }) }) }, `form-${prefix}${column}.${index}`))) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
6700
7454
  };
6701
7455
 
6702
7456
  const BooleanViewer = ({ schema, column, prefix, }) => {
6703
7457
  const { watch, formState: { errors }, } = useFormContext();
6704
- const { translate } = useSchemaContext();
6705
- const { required, gridColumn = "span 12", gridRow = "span 1" } = schema;
7458
+ const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
6706
7459
  const isRequired = required?.some((columnId) => columnId === column);
6707
7460
  const colLabel = `${prefix}${column}`;
6708
7461
  const value = watch(colLabel);
6709
- return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
6710
- gridRow, children: [jsx(Text, { children: value
6711
- ? translate.t(removeIndex(`${colLabel}.true`))
6712
- : translate.t(removeIndex(`${colLabel}.false`)) }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
7462
+ const formI18n = useFormI18n$1(column, prefix, schema);
7463
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
7464
+ gridRow, children: [jsx(Text, { children: value ? formI18n.t('true') : formI18n.t('false') }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
6713
7465
  };
6714
7466
 
6715
7467
  const CustomViewer = ({ column, schema, prefix }) => {
@@ -6726,19 +7478,22 @@ const CustomViewer = ({ column, schema, prefix }) => {
6726
7478
 
6727
7479
  const DateViewer = ({ column, schema, prefix }) => {
6728
7480
  const { watch, formState: { errors }, } = useFormContext();
6729
- const { translate, timezone } = useSchemaContext();
6730
- const { required, gridColumn = "span 4", gridRow = "span 1", displayDateFormat = "YYYY-MM-DD", } = schema;
7481
+ const { timezone } = useSchemaContext();
7482
+ const { required, gridColumn = 'span 4', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', } = schema;
6731
7483
  const isRequired = required?.some((columnId) => columnId === column);
6732
7484
  const colLabel = `${prefix}${column}`;
6733
7485
  const selectedDate = watch(colLabel);
6734
- const displayDate = dayjs(selectedDate).tz(timezone).format(displayDateFormat);
6735
- return (jsxs(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
6736
- gridRow, children: [jsxs(Text, { children: [" ", selectedDate !== undefined ? displayDate : ""] }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: translate.t(`${column}.field_required`) }))] }));
7486
+ const formI18n = useFormI18n$1(column, prefix, schema);
7487
+ const displayDate = dayjs(selectedDate)
7488
+ .tz(timezone)
7489
+ .format(displayDateFormat);
7490
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
7491
+ gridRow, children: [jsxs(Text, { children: [" ", selectedDate !== undefined ? displayDate : ''] }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
6737
7492
  };
6738
7493
 
6739
7494
  const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
6740
7495
  const { watch, formState: { errors }, } = useFormContext();
6741
- const formI18n = useFormI18n(column, prefix);
7496
+ const formI18n = useFormI18n$1(column, prefix);
6742
7497
  const { required } = schema;
6743
7498
  const isRequired = required?.some((columnId) => columnId === column);
6744
7499
  const { gridColumn = "span 12", gridRow = "span 1", renderDisplay } = schema;
@@ -6759,54 +7514,56 @@ const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
6759
7514
 
6760
7515
  const FileViewer = ({ column, schema, prefix }) => {
6761
7516
  const { watch } = useFormContext();
6762
- const { translate } = useSchemaContext();
6763
- const { required, gridColumn = "span 12", gridRow = "span 1", } = schema;
7517
+ const { required, gridColumn = 'span 12', gridRow = 'span 1', } = schema;
6764
7518
  const isRequired = required?.some((columnId) => columnId === column);
6765
7519
  const currentFiles = (watch(column) ?? []);
6766
- const colLabel = `${prefix}${column}`;
6767
- return (jsx(Field, { label: `${translate.t(`${colLabel}.field_label`)}`, required: isRequired, gridColumn: gridColumn, gridRow: gridRow, display: "grid", gridTemplateRows: "auto 1fr auto", alignItems: "stretch", children: jsx(Flex, { flexFlow: "column", gap: 1, children: currentFiles.map((file) => {
6768
- return (jsx(Card.Root, { variant: "subtle", children: jsxs(Card.Body, { gap: "2", display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [file.type.startsWith("image/") && (jsx(Image, { src: URL.createObjectURL(file), alt: file.name, boxSize: "50px", objectFit: "cover", borderRadius: "md", marginRight: "2" })), jsx(Box, { children: file.name })] }) }, file.name));
7520
+ const formI18n = useFormI18n$1(column, prefix, schema);
7521
+ return (jsx(Field, { label: formI18n.label(), required: isRequired, gridColumn: gridColumn, gridRow: gridRow, display: 'grid', gridTemplateRows: 'auto 1fr auto', alignItems: 'stretch', children: jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFiles.map((file) => {
7522
+ return (jsx(Card.Root, { variant: 'subtle', children: jsxs(Card.Body, { gap: "2", display: 'flex', flexFlow: 'row', alignItems: 'center', padding: '2', children: [file.type.startsWith('image/') && (jsx(Image, { src: URL.createObjectURL(file), alt: file.name, boxSize: "50px", objectFit: "cover", borderRadius: "md", marginRight: "2" })), jsx(Box, { children: file.name })] }) }, file.name));
6769
7523
  }) }) }));
6770
7524
  };
6771
7525
 
6772
7526
  const IdViewer = ({ column, schema, prefix, isMultiple = false, }) => {
6773
7527
  const { watch, formState: { errors }, } = useFormContext();
6774
7528
  const { idMap, translate } = useSchemaContext();
6775
- const { required, gridColumn = "span 12", gridRow = "span 1", renderDisplay, foreign_key, } = schema;
7529
+ const { required, gridColumn = 'span 12', gridRow = 'span 1', renderDisplay, foreign_key, } = schema;
6776
7530
  const isRequired = required?.some((columnId) => columnId === column);
6777
- const { display_column } = foreign_key;
7531
+ const formI18n = useFormI18n(column, prefix, schema);
6778
7532
  const colLabel = `${prefix}${column}`;
6779
7533
  const watchId = watch(colLabel);
6780
7534
  const watchIds = (watch(colLabel) ?? []);
6781
7535
  const getPickedValue = () => {
6782
7536
  if (Object.keys(idMap).length <= 0) {
6783
- return "";
7537
+ return '';
6784
7538
  }
6785
7539
  const record = idMap[watchId];
6786
7540
  if (record === undefined) {
6787
- return "";
7541
+ return '';
6788
7542
  }
6789
- return record[display_column];
7543
+ const rendered = renderDisplay
7544
+ ? renderDisplay(record)
7545
+ : defaultRenderDisplay(record);
7546
+ return typeof rendered === 'string' ? rendered : JSON.stringify(record);
6790
7547
  };
6791
- return (jsxs(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
6792
- gridRow, children: [isMultiple && (jsx(Flex, { flexFlow: "wrap", gap: 1, children: watchIds.map((id) => {
7548
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
7549
+ gridRow, children: [isMultiple && (jsx(Flex, { flexFlow: 'wrap', gap: 1, children: watchIds.map((id) => {
6793
7550
  const item = idMap[id];
6794
7551
  if (item === undefined) {
6795
7552
  return (jsx(Text, { children: translate.t(removeIndex(`${colLabel}.undefined`)) }, id));
6796
7553
  }
6797
- return (jsx(Tag, { closable: true, children: !!renderDisplay === true
7554
+ return (jsx(Tag, { closable: true, children: renderDisplay
6798
7555
  ? renderDisplay(item)
6799
- : item[display_column] }, id));
6800
- }) })), !isMultiple && jsx(Text, { children: getPickedValue() }), errors[`${colLabel}`] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
7556
+ : defaultRenderDisplay(item) }, id));
7557
+ }) })), !isMultiple && jsx(Text, { children: getPickedValue() }), errors[`${colLabel}`] && (jsx(Text, { color: 'red.400', children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
6801
7558
  };
6802
7559
 
6803
7560
  const NumberViewer = ({ schema, column, prefix, }) => {
6804
7561
  const { watch, formState: { errors }, } = useFormContext();
6805
- const { translate } = useSchemaContext();
6806
7562
  const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
6807
7563
  const isRequired = required?.some((columnId) => columnId === column);
6808
7564
  const colLabel = `${prefix}${column}`;
6809
7565
  const value = watch(colLabel);
7566
+ const formI18n = useFormI18n$1(column, prefix, schema);
6810
7567
  // Format the value for display if formatOptions are provided
6811
7568
  const formatValue = (val) => {
6812
7569
  if (val === undefined || val === null || val === '')
@@ -6825,90 +7582,49 @@ const NumberViewer = ({ schema, column, prefix, }) => {
6825
7582
  }
6826
7583
  return String(val);
6827
7584
  };
6828
- return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn, gridRow, children: [jsx(Text, { children: formatValue(value) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
7585
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, gridColumn, gridRow, children: [jsx(Text, { children: formatValue(value) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
6829
7586
  };
6830
7587
 
6831
7588
  const ObjectViewer = ({ schema, column, prefix }) => {
6832
- const { properties, gridColumn = "span 12", gridRow = "span 1", required, showLabel = true, } = schema;
6833
- const { translate } = useSchemaContext();
7589
+ const { properties, gridColumn = 'span 12', gridRow = 'span 1', required, showLabel = true, } = schema;
6834
7590
  const colLabel = `${prefix}${column}`;
6835
7591
  const isRequired = required?.some((columnId) => columnId === column);
7592
+ const formI18n = useFormI18n$1(column, prefix, schema);
6836
7593
  const { formState: { errors }, } = useFormContext();
6837
7594
  if (properties === undefined) {
6838
7595
  throw new Error(`properties is undefined when using ObjectInput`);
6839
7596
  }
6840
- return (jsxs(Box, { gridRow, gridColumn, children: [showLabel && (jsxs(Box, { as: "label", children: [`${translate.t(removeIndex(`${colLabel}.field_label`))}`, isRequired && jsx("span", { children: "*" })] })), jsx(Grid, { gap: "4", padding: "4", gridTemplateColumns: "repeat(12, 1fr)", autoFlow: "row", bgColor: { base: "colorPalette.100", _dark: "colorPalette.900" }, p: "1", borderRadius: "md", borderWidth: "thin", borderColor: {
6841
- base: "colorPalette.200",
6842
- _dark: "colorPalette.800",
7597
+ return (jsxs(Box, { gridRow, gridColumn, children: [showLabel && (jsxs(Box, { as: "label", children: [formI18n.label(), isRequired && jsx("span", { children: "*" })] })), jsx(Grid, { gap: "4", padding: '4', gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', bgColor: { base: 'colorPalette.100', _dark: 'colorPalette.900' }, p: '1', borderRadius: 'md', borderWidth: 'thin', borderColor: {
7598
+ base: 'colorPalette.200',
7599
+ _dark: 'colorPalette.800',
6843
7600
  }, children: Object.keys(properties ?? {}).map((key) => {
6844
7601
  return (
6845
7602
  // @ts-expect-error find suitable types
6846
7603
  jsx(ColumnViewer, { column: `${key}`,
6847
7604
  prefix: `${prefix}${column}.`,
6848
7605
  properties }, `form-objectviewer-${colLabel}-${key}`));
6849
- }) }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
7606
+ }) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
6850
7607
  };
6851
7608
 
6852
- const RecordInput = ({ column, schema, prefix }) => {
6853
- const { formState: { errors }, setValue, getValues, } = useFormContext();
6854
- const { translate } = useSchemaContext();
6855
- const { required, gridColumn = "span 12", gridRow = "span 1" } = schema;
7609
+ const RecordViewer = ({ column, schema, prefix }) => {
7610
+ const { formState: { errors }, getValues, } = useFormContext();
7611
+ const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
6856
7612
  const isRequired = required?.some((columnId) => columnId === column);
6857
7613
  const entries = Object.entries(getValues(column) ?? {});
6858
- const [showNewEntries, setShowNewEntries] = useState(false);
6859
- const [newKey, setNewKey] = useState();
6860
- const [newValue, setNewValue] = useState();
6861
- return (jsxs(Field, { label: `${translate.t(`${column}.field_label`)}`, required: isRequired, alignItems: "stretch", gridColumn, gridRow, children: [entries.map(([key, value]) => {
6862
- return (jsxs(Grid, { templateColumns: "1fr 1fr auto", gap: 1, children: [jsx(Input, { value: key, onChange: (e) => {
6863
- const filtered = entries.filter(([target]) => {
6864
- return target !== key;
6865
- });
6866
- setValue(column, Object.fromEntries([...filtered, [e.target.value, value]]));
6867
- }, autoComplete: "off" }), jsx(Input, { value: value, onChange: (e) => {
6868
- setValue(column, {
6869
- ...getValues(column),
6870
- [key]: e.target.value,
6871
- });
6872
- }, autoComplete: "off" }), jsx(IconButton, { variant: "ghost", onClick: () => {
6873
- const filtered = entries.filter(([target]) => {
6874
- return target !== key;
6875
- });
6876
- setValue(column, Object.fromEntries([...filtered]));
6877
- }, children: jsx(CgClose, {}) })] }));
6878
- }), jsx(Show, { when: showNewEntries, children: jsxs(Card.Root, { children: [jsx(Card.Body, { gap: "2", children: jsxs(Grid, { templateColumns: "1fr 1fr auto", gap: 1, children: [jsx(Input, { value: newKey, onChange: (e) => {
6879
- setNewKey(e.target.value);
6880
- }, autoComplete: "off" }), jsx(Input, { value: newValue, onChange: (e) => {
6881
- setNewValue(e.target.value);
6882
- }, autoComplete: "off" })] }) }), jsxs(Card.Footer, { justifyContent: "flex-end", children: [jsx(IconButton, { variant: "subtle", onClick: () => {
6883
- setShowNewEntries(false);
6884
- setNewKey(undefined);
6885
- setNewValue(undefined);
6886
- }, children: jsx(CgClose, {}) }), jsx(Button, { onClick: () => {
6887
- if (!!newKey === false) {
6888
- setShowNewEntries(false);
6889
- setNewKey(undefined);
6890
- setNewValue(undefined);
6891
- return;
6892
- }
6893
- setValue(column, Object.fromEntries([...entries, [newKey, newValue]]));
6894
- setShowNewEntries(false);
6895
- setNewKey(undefined);
6896
- setNewValue(undefined);
6897
- }, children: translate.t(`${column}.save`) })] })] }) }), jsx(Button, { onClick: () => {
6898
- setShowNewEntries(true);
6899
- setNewKey(undefined);
6900
- setNewValue(undefined);
6901
- }, children: translate.t(`${column}.addNew`) }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: translate.t(`${column}.field_required`) }))] }));
7614
+ const formI18n = useFormI18n$1(column, prefix, schema);
7615
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn, gridRow, children: [entries.length === 0 ? (jsx(Text, { color: "gray.500", children: "No entries" })) : (jsx(Grid, { templateColumns: '1fr 1fr', gap: 2, children: entries.map(([key, value]) => {
7616
+ return (jsxs(Grid, { templateColumns: '1fr 1fr', gap: 2, children: [jsxs(Text, { fontWeight: "medium", children: [key, ":"] }), jsx(Text, { children: String(value ?? '') })] }, key));
7617
+ }) })), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
6902
7618
  };
6903
7619
 
6904
7620
  const StringViewer = ({ column, schema, prefix, }) => {
6905
7621
  const { watch, formState: { errors }, } = useFormContext();
6906
- const { translate } = useSchemaContext();
6907
- const { required, gridColumn = "span 12", gridRow = "span 1" } = schema;
7622
+ const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
6908
7623
  const isRequired = required?.some((columnId) => columnId === column);
6909
7624
  const colLabel = `${prefix}${column}`;
6910
7625
  const value = watch(colLabel);
6911
- return (jsx(Fragment, { children: jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn ?? "span 4", gridRow: gridRow ?? "span 1", children: [jsx(Text, { children: value }), errors[colLabel] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }) }));
7626
+ const formI18n = useFormI18n$1(column, prefix, schema);
7627
+ return (jsx(Fragment, { children: jsxs(Field, { label: formI18n.label(), required: isRequired, gridColumn: gridColumn ?? 'span 4', gridRow: gridRow ?? 'span 1', children: [jsx(Text, { children: value }), errors[colLabel] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }) }));
6912
7628
  };
6913
7629
 
6914
7630
  const TagViewer = ({ column, schema, prefix }) => {
@@ -6998,40 +7714,44 @@ const TagViewer = ({ column, schema, prefix }) => {
6998
7714
 
6999
7715
  const TextAreaViewer = ({ column, schema, prefix, }) => {
7000
7716
  const { watch, formState: { errors }, } = useFormContext();
7001
- const { translate } = useSchemaContext();
7002
- const { required, gridColumn = "span 12", gridRow = "span 1" } = schema;
7717
+ const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
7003
7718
  const isRequired = required?.some((columnId) => columnId === column);
7004
7719
  const colLabel = `${prefix}${column}`;
7005
7720
  const value = watch(colLabel);
7006
- return (jsx(Fragment, { children: jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn, gridRow: gridRow, children: [jsx(Text, { whiteSpace: "pre-wrap", children: value }), " ", errors[colLabel] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }) }));
7721
+ const formI18n = useFormI18n$1(column, prefix, schema);
7722
+ return (jsx(Fragment, { children: jsxs(Field, { label: formI18n.label(), required: isRequired, gridColumn: gridColumn, gridRow: gridRow, children: [jsx(Text, { whiteSpace: "pre-wrap", children: value }), ' ', errors[colLabel] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }) }));
7007
7723
  };
7008
7724
 
7009
7725
  const TimeViewer = ({ column, schema, prefix }) => {
7010
7726
  const { watch, formState: { errors }, } = useFormContext();
7011
- const { translate, timezone } = useSchemaContext();
7012
- const { required, gridColumn = "span 12", gridRow = "span 1", displayTimeFormat = "hh:mm A", } = schema;
7727
+ const { timezone } = useSchemaContext();
7728
+ const { required, gridColumn = 'span 12', gridRow = 'span 1', displayTimeFormat = 'hh:mm A', } = schema;
7013
7729
  const isRequired = required?.some((columnId) => columnId === column);
7014
7730
  const colLabel = `${prefix}${column}`;
7015
7731
  const selectedDate = watch(colLabel);
7732
+ const formI18n = useFormI18n$1(column, prefix, schema);
7016
7733
  const displayedTime = dayjs(`1970-01-01T${selectedDate}`)
7017
7734
  .tz(timezone)
7018
7735
  .isValid()
7019
7736
  ? dayjs(`1970-01-01T${selectedDate}`).tz(timezone).format(displayTimeFormat)
7020
- : "";
7021
- return (jsxs(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
7022
- gridRow, children: [jsx(Text, { children: displayedTime }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: translate.t(`${column}.field_required`) }))] }));
7737
+ : '';
7738
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
7739
+ gridRow, children: [jsx(Text, { children: displayedTime }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
7023
7740
  };
7024
7741
 
7025
7742
  const DateTimeViewer = ({ column, schema, prefix }) => {
7026
7743
  const { watch, formState: { errors }, } = useFormContext();
7027
- const { translate, timezone } = useSchemaContext();
7028
- const { required, gridColumn = "span 4", gridRow = "span 1", displayDateFormat = "YYYY-MM-DD HH:mm:ss", } = schema;
7744
+ const { timezone } = useSchemaContext();
7745
+ const { required, gridColumn = 'span 4', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD HH:mm:ss', } = schema;
7029
7746
  const isRequired = required?.some((columnId) => columnId === column);
7030
7747
  const colLabel = `${prefix}${column}`;
7031
7748
  const selectedDate = watch(colLabel);
7032
- const displayDate = dayjs(selectedDate).tz(timezone).format(displayDateFormat);
7033
- return (jsxs(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
7034
- gridRow, children: [jsxs(Text, { children: [" ", selectedDate !== undefined ? displayDate : ""] }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: translate.t(`${column}.field_required`) }))] }));
7749
+ const formI18n = useFormI18n$1(column, prefix, schema);
7750
+ const displayDate = dayjs(selectedDate)
7751
+ .tz(timezone)
7752
+ .format(displayDateFormat);
7753
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
7754
+ gridRow, children: [jsxs(Text, { children: [" ", selectedDate !== undefined ? displayDate : ''] }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
7035
7755
  };
7036
7756
 
7037
7757
  const SchemaViewer = ({ schema, prefix, column, }) => {
@@ -7072,7 +7792,7 @@ const SchemaViewer = ({ schema, prefix, column, }) => {
7072
7792
  if (innerProperties) {
7073
7793
  return jsx(ObjectViewer, { schema: colSchema, prefix, column });
7074
7794
  }
7075
- return jsx(RecordInput, { schema: colSchema, prefix, column });
7795
+ return jsx(RecordViewer, { schema: colSchema, prefix, column });
7076
7796
  }
7077
7797
  if (type === 'array') {
7078
7798
  if (variant === 'id-picker') {
@@ -7120,7 +7840,7 @@ const ColumnViewer = ({ column, properties, prefix, }) => {
7120
7840
  };
7121
7841
 
7122
7842
  const SubmitButton = () => {
7123
- const { translate, setValidatedData, setIsError, setIsConfirming, setError, schema, requireConfirmation, onFormSubmit, } = useSchemaContext();
7843
+ const { translate, setValidatedData, setIsError, setIsConfirming, requireConfirmation, onFormSubmit, formButtonLabels, } = useSchemaContext();
7124
7844
  const methods = useFormContext();
7125
7845
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7126
7846
  const onValid = (data) => {
@@ -7149,11 +7869,11 @@ const SubmitButton = () => {
7149
7869
  };
7150
7870
  return (jsx(Button$1, { onClick: () => {
7151
7871
  methods.handleSubmit(onValid)();
7152
- }, formNoValidate: true, children: translate.t('submit') }));
7872
+ }, formNoValidate: true, children: formButtonLabels?.submit ?? translate.t('submit') }));
7153
7873
  };
7154
7874
 
7155
7875
  const FormBody = () => {
7156
- const { schema, order, ignore, include, translate, isSuccess, setIsSuccess, isError, setIsError, isSubmiting, setIsSubmiting, isConfirming, setIsConfirming, validatedData, setValidatedData, error, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, onFormSubmit, } = useSchemaContext();
7876
+ const { schema, order, ignore, include, translate, isSuccess, setIsSuccess, isError, setIsError, isSubmiting, setIsSubmiting, isConfirming, setIsConfirming, validatedData, setValidatedData, error, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, onFormSubmit, formButtonLabels, } = useSchemaContext();
7157
7877
  const { showSubmitButton, showResetButton } = displayConfig;
7158
7878
  const methods = useFormContext();
7159
7879
  const { properties } = schema;
@@ -7183,7 +7903,7 @@ const FormBody = () => {
7183
7903
  if (customSuccessRenderer) {
7184
7904
  return customSuccessRenderer(resetHandler);
7185
7905
  }
7186
- return (jsxs(Flex, { flexFlow: 'column', gap: "2", children: [jsxs(Alert.Root, { status: "success", children: [jsx(Alert.Indicator, {}), jsx(Alert.Content, { children: jsx(Alert.Title, { children: translate.t('submit_success') }) })] }), jsx(Flex, { justifyContent: 'end', children: jsx(Button$1, { onClick: resetHandler, formNoValidate: true, children: translate.t('submit_again') }) })] }));
7906
+ return (jsxs(Flex, { flexFlow: 'column', gap: "2", children: [jsxs(Alert.Root, { status: "success", children: [jsx(Alert.Indicator, {}), jsx(Alert.Content, { children: jsx(Alert.Title, { children: translate.t('submit_success') }) })] }), jsx(Flex, { justifyContent: 'end', children: jsx(Button$1, { onClick: resetHandler, formNoValidate: true, children: formButtonLabels?.submitAgain ?? translate.t('submit_again') }) })] }));
7187
7907
  }
7188
7908
  if (isConfirming) {
7189
7909
  return (jsxs(Flex, { flexFlow: 'column', gap: "2", children: [jsx(Grid, { gap: 4, gridTemplateColumns: 'repeat(12, 1fr)', gridTemplateRows: 'repeat(12, max-content)', autoFlow: 'row', children: ordered.map((column) => {
@@ -7194,9 +7914,9 @@ const FormBody = () => {
7194
7914
  properties: properties, prefix: ``, column }, `form-viewer-${column}`));
7195
7915
  }) }), jsxs(Flex, { justifyContent: 'end', gap: '2', children: [jsx(Button$1, { onClick: () => {
7196
7916
  setIsConfirming(false);
7197
- }, variant: 'subtle', children: translate.t('cancel') }), jsx(Button$1, { onClick: () => {
7917
+ }, variant: 'subtle', children: formButtonLabels?.cancel ?? translate.t('cancel') }), jsx(Button$1, { onClick: () => {
7198
7918
  onFormSubmit(validatedData);
7199
- }, children: translate.t('confirm') })] }), isSubmiting && (jsx(Box, { pos: "absolute", inset: "0", bg: "bg/80", children: jsx(Center, { h: "full", children: jsx(Spinner, { color: "teal.500" }) }) })), isError && customErrorRenderer && customErrorRenderer(error)] }));
7919
+ }, children: formButtonLabels?.confirm ?? translate.t('confirm') })] }), isSubmiting && (jsx(Box, { pos: "absolute", inset: "0", bg: "bg/80", children: jsx(Center, { h: "full", children: jsx(Spinner, { color: "teal.500" }) }) })), isError && customErrorRenderer && customErrorRenderer(error)] }));
7200
7920
  }
7201
7921
  return (jsxs(Flex, { flexFlow: 'column', gap: "2", children: [jsx(Grid, { gap: "4", gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: ordered.map((column) => {
7202
7922
  return (jsx(ColumnRenderer
@@ -7206,7 +7926,7 @@ const FormBody = () => {
7206
7926
  properties: properties, prefix: ``, parentRequired: schema.required, column }, `form-input-${column}`));
7207
7927
  }) }), jsxs(Flex, { justifyContent: 'end', gap: "2", children: [showResetButton && (jsx(Button$1, { onClick: () => {
7208
7928
  methods.reset();
7209
- }, variant: 'subtle', children: translate.t('reset') })), showSubmitButton && jsx(SubmitButton, {})] }), isError && customErrorRenderer && customErrorRenderer(error)] }));
7929
+ }, variant: 'subtle', children: formButtonLabels?.reset ?? translate.t('reset') })), showSubmitButton && jsx(SubmitButton, {})] }), isError && customErrorRenderer && customErrorRenderer(error)] }));
7210
7930
  };
7211
7931
 
7212
7932
  const FormTitle = () => {
@@ -7525,7 +8245,6 @@ const TableDataDisplay = ({ colorPalette, emptyComponent, }) => {
7525
8245
  return `minmax(${size}px, ${(size / totalWidths) * 100}%)`;
7526
8246
  })
7527
8247
  .join(' ');
7528
- console.log({ columnWidths }, 'hadfg');
7529
8248
  const cellProps = {
7530
8249
  flex: '1 0 0%',
7531
8250
  overflow: 'auto',
@@ -7542,22 +8261,22 @@ const TableDataDisplay = ({ colorPalette, emptyComponent, }) => {
7542
8261
  }
7543
8262
  return (jsxs(Grid, { templateColumns: `${columnWidths}`, overflow: 'auto', borderWidth: '1px', color: { base: 'colorPalette.900', _dark: 'colorPalette.100' }, borderColor: { base: 'colorPalette.200', _dark: 'colorPalette.800' }, colorPalette, children: [jsx(Grid, { templateColumns: `${columnWidths}`, column: `1/span ${columns.length}`, bg: { base: 'colorPalette.200', _dark: 'colorPalette.800' }, colorPalette, children: columnHeaders.map((header) => {
7544
8263
  const columnDef = columnsMap[header];
7545
- return (jsx(Box, { flex: '1 0 0%', paddingX: '2', py: '1', overflow: 'auto', textOverflow: 'ellipsis', children: columnDef?.meta?.displayName ?? header }));
7546
- }) }), data.map((record) => {
7547
- return (jsx(Fragment, { children: columnHeaders.map((header) => {
8264
+ return (jsx(Box, { flex: '1 0 0%', paddingX: '2', py: '1', overflow: 'auto', textOverflow: 'ellipsis', children: columnDef?.meta?.displayName ?? header }, `chakra-table-header-${header}`));
8265
+ }) }), data.map((record, recordIndex) => {
8266
+ return (jsx(Box, { display: "contents", children: columnHeaders.map((header) => {
7548
8267
  const { cell } = columnsMap[header];
7549
8268
  const value = record[header];
7550
8269
  if (!!record === false) {
7551
- return (jsx(Box, { ...cellProps }));
8270
+ return (jsx(Box, { ...cellProps }, `chakra-table-cell-${recordIndex}-${header}`));
7552
8271
  }
7553
8272
  if (cell) {
7554
- return (jsx(Box, { ...cellProps, children: cell({ row: { original: record } }) }));
8273
+ return (jsx(Box, { ...cellProps, children: cell({ row: { original: record } }) }, `chakra-table-cell-${recordIndex}-${header}`));
7555
8274
  }
7556
8275
  if (typeof value === 'object') {
7557
- return (jsx(Box, { ...cellProps, children: jsx(RecordDisplay, { object: value }) }));
8276
+ return (jsx(Box, { ...cellProps, children: jsx(RecordDisplay, { object: value }) }, `chakra-table-cell-${recordIndex}-${header}`));
7558
8277
  }
7559
- return jsx(Box, { ...cellProps, children: value });
7560
- }) }));
8278
+ return (jsx(Box, { ...cellProps, children: value }, `chakra-table-cell-${recordIndex}-${header}`));
8279
+ }) }, `chakra-table-record-${recordIndex}`));
7561
8280
  })] }));
7562
8281
  };
7563
8282
 
@@ -7710,19 +8429,19 @@ const DataDisplay = ({ variant = '' }) => {
7710
8429
  return cell.id === `${rowId}_${column.id}`;
7711
8430
  });
7712
8431
  if (column.columns.length > 0) {
7713
- return (jsxs(Card.Root, { margin: '1', gridColumn: 'span 12', children: [jsx(Card.Header, { color: 'gray.400', children: column.columnDef.meta?.displayName ?? column.id }), jsx(Card.Body, { display: 'grid', gap: '4', gridTemplateColumns: 'repeat(12, 1fr)', children: column.columns.map((column) => {
7714
- if (!column.getIsVisible()) {
7715
- return jsx(Fragment, {});
8432
+ return (jsxs(Card.Root, { margin: '1', gridColumn: 'span 12', children: [jsx(Card.Header, { color: 'gray.400', children: column.columnDef.meta?.displayName ?? column.id }), jsx(Card.Body, { display: 'grid', gap: '4', gridTemplateColumns: 'repeat(12, 1fr)', children: column.columns.map((subColumn) => {
8433
+ if (!subColumn.getIsVisible()) {
8434
+ return null;
7716
8435
  }
7717
8436
  const foundCell = row
7718
8437
  .getVisibleCells()
7719
8438
  .find((cell) => {
7720
- return cell.id === `${rowId}_${column.id}`;
8439
+ return cell.id === `${rowId}_${subColumn.id}`;
7721
8440
  });
7722
- return jsx(CellRenderer, { cell: foundCell });
7723
- }) })] }, `chakra-table-card-${childCell?.id}`));
8441
+ return (jsx(CellRenderer, { cell: foundCell }, `chakra-table-cell-${rowId}-${subColumn.id}`));
8442
+ }) })] }, `chakra-table-card-${rowId}-${column.id}`));
7724
8443
  }
7725
- return jsx(CellRenderer, { cell: childCell });
8444
+ return (jsx(CellRenderer, { cell: childCell }, `chakra-table-cell-${rowId}-${column.id}`));
7726
8445
  }) }) }, `chakra-table-card-${rowId}`));
7727
8446
  }) }));
7728
8447
  };
@@ -8017,4 +8736,4 @@ function DataTableServer({ columns, enableRowSelection = true, enableMultiRowSel
8017
8736
  }, children: jsx(DataTableServerContext.Provider, { value: { url, query }, children: children }) }));
8018
8737
  }
8019
8738
 
8020
- export { CardHeader, DataDisplay, DataTable, DataTableServer, DefaultCardTitle, DefaultForm, DefaultTable, DefaultTableServer, DensityToggleButton, EditSortingButton, EmptyState, ErrorAlert, FilterDialog, FormBody, FormRoot, FormTitle, GlobalFilter, MediaLibraryBrowser, PageSizeControl, Pagination, RecordDisplay, ReloadButton, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, RowCountText, SelectAllRowsToggle, Table, TableBody, TableCardContainer, TableCards, TableComponent, TableControls, TableDataDisplay, TableFilter, TableFilterTags, TableFooter, TableHeader, TableLoadingComponent, TableSelector, TableSorter, TableViewer, TextCell, ViewDialog, buildErrorMessages, buildFieldErrors, buildRequiredErrors, convertToAjvErrorsFormat, createErrorMessage, getColumns, getMultiDates, getRangeDates, idPickerSanityCheck, useDataTable, useDataTableContext, useDataTableServer, useForm, widthSanityCheck };
8739
+ export { CardHeader, DataDisplay, DataTable, DataTableServer, DatePickerInput, DefaultCardTitle, DefaultForm, DefaultTable, DefaultTableServer, DensityToggleButton, EditSortingButton, EmptyState, ErrorAlert, FilterDialog, FormBody, FormRoot, FormTitle, GlobalFilter, MediaLibraryBrowser, PageSizeControl, Pagination, RecordDisplay, ReloadButton, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, RowCountText, SelectAllRowsToggle, Table, TableBody, TableCardContainer, TableCards, TableComponent, TableControls, TableDataDisplay, TableFilter, TableFilterTags, TableFooter, TableHeader, TableLoadingComponent, TableSelector, TableSorter, TableViewer, TextCell, ViewDialog, buildErrorMessages, buildFieldErrors, buildRequiredErrors, convertToAjvErrorsFormat, createErrorMessage, defaultRenderDisplay, getColumns, getMultiDates, getRangeDates, idPickerSanityCheck, useDataTable, useDataTableContext, useDataTableServer, useForm, widthSanityCheck };