@openg2p/registry-widgets 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -670,23 +670,235 @@ const getFormattedNumberLength = (value, format) => {
670
670
  return formatted.length;
671
671
  };
672
672
 
673
+ /**
674
+ * Date input utilities for parsing, formatting, and validation
675
+ */
676
+ /**
677
+ * Parse date string in various formats to Date object
678
+ */
679
+ const parseDate = (dateString) => {
680
+ if (!dateString)
681
+ return null;
682
+ // Try ISO format first (YYYY-MM-DD)
683
+ const isoMatch = dateString.match(/^(\d{4})-(\d{2})-(\d{2})/);
684
+ if (isoMatch) {
685
+ const date = new Date(parseInt(isoMatch[1]), parseInt(isoMatch[2]) - 1, parseInt(isoMatch[3]));
686
+ if (!isNaN(date.getTime())) {
687
+ return date;
688
+ }
689
+ }
690
+ // Try common formats
691
+ const date = new Date(dateString);
692
+ if (!isNaN(date.getTime())) {
693
+ return date;
694
+ }
695
+ return null;
696
+ };
697
+ /**
698
+ * Format date to ISO string (YYYY-MM-DD) for storage
699
+ */
700
+ const formatDateToISO = (date) => {
701
+ if (!date)
702
+ return '';
703
+ let dateObj;
704
+ if (date instanceof Date) {
705
+ dateObj = date;
706
+ }
707
+ else if (typeof date === 'string') {
708
+ const parsed = parseDate(date);
709
+ if (!parsed)
710
+ return '';
711
+ dateObj = parsed;
712
+ }
713
+ else {
714
+ return '';
715
+ }
716
+ if (isNaN(dateObj.getTime()))
717
+ return '';
718
+ const year = dateObj.getFullYear();
719
+ const month = String(dateObj.getMonth() + 1).padStart(2, '0');
720
+ const day = String(dateObj.getDate()).padStart(2, '0');
721
+ return `${year}-${month}-${day}`;
722
+ };
723
+ /**
724
+ * Format date to custom format string
725
+ */
726
+ const formatDateToString = (date, format) => {
727
+ if (!date || !format)
728
+ return '';
729
+ let dateObj;
730
+ if (date instanceof Date) {
731
+ dateObj = date;
732
+ }
733
+ else if (typeof date === 'string') {
734
+ const parsed = parseDate(date);
735
+ if (!parsed)
736
+ return '';
737
+ dateObj = parsed;
738
+ }
739
+ else {
740
+ return '';
741
+ }
742
+ if (isNaN(dateObj.getTime()))
743
+ return '';
744
+ const day = String(dateObj.getDate()).padStart(2, '0');
745
+ const month = String(dateObj.getMonth() + 1).padStart(2, '0');
746
+ const year = dateObj.getFullYear();
747
+ const yearShort = year.toString().slice(-2);
748
+ return format
749
+ .replace(/DD/g, day)
750
+ .replace(/MM/g, month)
751
+ .replace(/YYYY/g, year.toString())
752
+ .replace(/YY/g, yearShort);
753
+ };
754
+ /**
755
+ * Parse custom format string to Date object
756
+ */
757
+ const parseDateFromFormat = (dateString, format) => {
758
+ if (!dateString || !format)
759
+ return null;
760
+ // Extract day, month, year positions from format
761
+ const dayIndex = format.indexOf('DD');
762
+ const monthIndex = format.indexOf('MM');
763
+ const yearIndex = format.indexOf('YYYY');
764
+ const yearShortIndex = format.indexOf('YY');
765
+ if (dayIndex === -1 || monthIndex === -1 || (yearIndex === -1 && yearShortIndex === -1)) {
766
+ // Fallback to standard Date parsing
767
+ return parseDate(dateString);
768
+ }
769
+ try {
770
+ let day = '';
771
+ let month = '';
772
+ let year = '';
773
+ // Extract day (2 digits)
774
+ if (dayIndex !== -1) {
775
+ day = dateString.substring(dayIndex, dayIndex + 2);
776
+ }
777
+ // Extract month (2 digits)
778
+ if (monthIndex !== -1) {
779
+ month = dateString.substring(monthIndex, monthIndex + 2);
780
+ }
781
+ // Extract year (4 digits or 2 digits)
782
+ if (yearIndex !== -1) {
783
+ year = dateString.substring(yearIndex, yearIndex + 4);
784
+ }
785
+ else if (yearShortIndex !== -1) {
786
+ const yearShort = dateString.substring(yearShortIndex, yearShortIndex + 2);
787
+ const currentYear = new Date().getFullYear();
788
+ const currentCentury = Math.floor(currentYear / 100) * 100;
789
+ const parsedYear = parseInt(yearShort);
790
+ // Assume 20xx for years 00-50, 19xx for years 51-99
791
+ year = parsedYear <= 50
792
+ ? (currentCentury + parsedYear).toString()
793
+ : (currentCentury - 100 + parsedYear).toString();
794
+ }
795
+ if (day && month && year) {
796
+ const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
797
+ if (!isNaN(date.getTime())) {
798
+ return date;
799
+ }
800
+ }
801
+ }
802
+ catch {
803
+ // Fallback to standard parsing
804
+ }
805
+ return parseDate(dateString);
806
+ };
807
+ /**
808
+ * Get min date based on constraint type
809
+ */
810
+ const getMinDate = (constraint, minDate) => {
811
+ if (minDate) {
812
+ // If it's 'today', return today's date
813
+ if (minDate === 'today') {
814
+ return formatDateToISO(new Date());
815
+ }
816
+ return minDate;
817
+ }
818
+ if (constraint === 'past-only') {
819
+ // No minimum for past-only (can select any past date)
820
+ return undefined;
821
+ }
822
+ if (constraint === 'future-only') {
823
+ // Minimum is today for future-only
824
+ return formatDateToISO(new Date());
825
+ }
826
+ return undefined;
827
+ };
828
+ /**
829
+ * Get max date based on constraint type
830
+ */
831
+ const getMaxDate = (constraint, maxDate) => {
832
+ if (maxDate) {
833
+ // If it's 'today', return today's date
834
+ if (maxDate === 'today') {
835
+ return formatDateToISO(new Date());
836
+ }
837
+ return maxDate;
838
+ }
839
+ if (constraint === 'past-only') {
840
+ // Maximum is today for past-only
841
+ return formatDateToISO(new Date());
842
+ }
843
+ if (constraint === 'future-only') {
844
+ // No maximum for future-only (can select any future date)
845
+ return undefined;
846
+ }
847
+ return undefined;
848
+ };
849
+ /**
850
+ * Validate date constraints
851
+ */
852
+ const validateDateConstraints = (date, minDate, maxDate, constraint) => {
853
+ if (!date)
854
+ return null;
855
+ const dateObj = date instanceof Date ? date : parseDate(date);
856
+ if (!dateObj)
857
+ return null;
858
+ const dateISO = formatDateToISO(dateObj);
859
+ const todayISO = formatDateToISO(new Date());
860
+ // Check constraint type
861
+ if (constraint === 'past-only') {
862
+ if (dateISO > todayISO) {
863
+ return 'Date must be in the past';
864
+ }
865
+ }
866
+ else if (constraint === 'future-only') {
867
+ if (dateISO < todayISO) {
868
+ return 'Date must be in the future';
869
+ }
870
+ }
871
+ // Check minDate
872
+ const effectiveMinDate = getMinDate(constraint, minDate);
873
+ if (effectiveMinDate && dateISO < effectiveMinDate) {
874
+ return `Date must be on or after ${effectiveMinDate}`;
875
+ }
876
+ // Check maxDate
877
+ const effectiveMaxDate = getMaxDate(constraint, maxDate);
878
+ if (effectiveMaxDate && dateISO > effectiveMaxDate) {
879
+ return `Date must be on or before ${effectiveMaxDate}`;
880
+ }
881
+ return null;
882
+ };
883
+
673
884
  /**
674
885
  * Format date value
675
886
  */
676
887
  const formatDate = (value, format) => {
677
- if (!value || !format?.dateFormat) {
678
- return value?.toString() || '';
888
+ if (!value) {
889
+ return '';
679
890
  }
891
+ const dateFormat = format?.dateFormat || 'DD-MM-YYYY';
680
892
  try {
681
- const date = new Date(value);
682
- if (isNaN(date.getTime())) {
893
+ const date = typeof value === 'string' ? parseDate(value) : new Date(value);
894
+ if (!date || isNaN(date.getTime())) {
683
895
  return value?.toString() || '';
684
896
  }
685
897
  // Simple date formatting (can be enhanced with date-fns or similar)
686
898
  const day = date.getDate().toString().padStart(2, '0');
687
899
  const month = (date.getMonth() + 1).toString().padStart(2, '0');
688
900
  const year = date.getFullYear();
689
- return format.dateFormat
901
+ return dateFormat
690
902
  .replace('DD', day)
691
903
  .replace('MM', month)
692
904
  .replace('YYYY', year.toString())
@@ -6796,217 +7008,6 @@ const BooleanWidget = ({ config }) => {
6796
7008
  : 'bg-white text-gray-700 border-gray-300'} ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50'}`, style: { borderRadius: '15px' }, children: falseLabel })] }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
6797
7009
  };
6798
7010
 
6799
- /**
6800
- * Date input utilities for parsing, formatting, and validation
6801
- */
6802
- /**
6803
- * Parse date string in various formats to Date object
6804
- */
6805
- const parseDate = (dateString) => {
6806
- if (!dateString)
6807
- return null;
6808
- // Try ISO format first (YYYY-MM-DD)
6809
- const isoMatch = dateString.match(/^(\d{4})-(\d{2})-(\d{2})/);
6810
- if (isoMatch) {
6811
- const date = new Date(parseInt(isoMatch[1]), parseInt(isoMatch[2]) - 1, parseInt(isoMatch[3]));
6812
- if (!isNaN(date.getTime())) {
6813
- return date;
6814
- }
6815
- }
6816
- // Try common formats
6817
- const date = new Date(dateString);
6818
- if (!isNaN(date.getTime())) {
6819
- return date;
6820
- }
6821
- return null;
6822
- };
6823
- /**
6824
- * Format date to ISO string (YYYY-MM-DD) for storage
6825
- */
6826
- const formatDateToISO = (date) => {
6827
- if (!date)
6828
- return '';
6829
- let dateObj;
6830
- if (date instanceof Date) {
6831
- dateObj = date;
6832
- }
6833
- else if (typeof date === 'string') {
6834
- const parsed = parseDate(date);
6835
- if (!parsed)
6836
- return '';
6837
- dateObj = parsed;
6838
- }
6839
- else {
6840
- return '';
6841
- }
6842
- if (isNaN(dateObj.getTime()))
6843
- return '';
6844
- const year = dateObj.getFullYear();
6845
- const month = String(dateObj.getMonth() + 1).padStart(2, '0');
6846
- const day = String(dateObj.getDate()).padStart(2, '0');
6847
- return `${year}-${month}-${day}`;
6848
- };
6849
- /**
6850
- * Format date to custom format string
6851
- */
6852
- const formatDateToString = (date, format) => {
6853
- if (!date || !format)
6854
- return '';
6855
- let dateObj;
6856
- if (date instanceof Date) {
6857
- dateObj = date;
6858
- }
6859
- else if (typeof date === 'string') {
6860
- const parsed = parseDate(date);
6861
- if (!parsed)
6862
- return '';
6863
- dateObj = parsed;
6864
- }
6865
- else {
6866
- return '';
6867
- }
6868
- if (isNaN(dateObj.getTime()))
6869
- return '';
6870
- const day = String(dateObj.getDate()).padStart(2, '0');
6871
- const month = String(dateObj.getMonth() + 1).padStart(2, '0');
6872
- const year = dateObj.getFullYear();
6873
- const yearShort = year.toString().slice(-2);
6874
- return format
6875
- .replace(/DD/g, day)
6876
- .replace(/MM/g, month)
6877
- .replace(/YYYY/g, year.toString())
6878
- .replace(/YY/g, yearShort);
6879
- };
6880
- /**
6881
- * Parse custom format string to Date object
6882
- */
6883
- const parseDateFromFormat = (dateString, format) => {
6884
- if (!dateString || !format)
6885
- return null;
6886
- // Extract day, month, year positions from format
6887
- const dayIndex = format.indexOf('DD');
6888
- const monthIndex = format.indexOf('MM');
6889
- const yearIndex = format.indexOf('YYYY');
6890
- const yearShortIndex = format.indexOf('YY');
6891
- if (dayIndex === -1 || monthIndex === -1 || (yearIndex === -1 && yearShortIndex === -1)) {
6892
- // Fallback to standard Date parsing
6893
- return parseDate(dateString);
6894
- }
6895
- try {
6896
- let day = '';
6897
- let month = '';
6898
- let year = '';
6899
- // Extract day (2 digits)
6900
- if (dayIndex !== -1) {
6901
- day = dateString.substring(dayIndex, dayIndex + 2);
6902
- }
6903
- // Extract month (2 digits)
6904
- if (monthIndex !== -1) {
6905
- month = dateString.substring(monthIndex, monthIndex + 2);
6906
- }
6907
- // Extract year (4 digits or 2 digits)
6908
- if (yearIndex !== -1) {
6909
- year = dateString.substring(yearIndex, yearIndex + 4);
6910
- }
6911
- else if (yearShortIndex !== -1) {
6912
- const yearShort = dateString.substring(yearShortIndex, yearShortIndex + 2);
6913
- const currentYear = new Date().getFullYear();
6914
- const currentCentury = Math.floor(currentYear / 100) * 100;
6915
- const parsedYear = parseInt(yearShort);
6916
- // Assume 20xx for years 00-50, 19xx for years 51-99
6917
- year = parsedYear <= 50
6918
- ? (currentCentury + parsedYear).toString()
6919
- : (currentCentury - 100 + parsedYear).toString();
6920
- }
6921
- if (day && month && year) {
6922
- const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
6923
- if (!isNaN(date.getTime())) {
6924
- return date;
6925
- }
6926
- }
6927
- }
6928
- catch {
6929
- // Fallback to standard parsing
6930
- }
6931
- return parseDate(dateString);
6932
- };
6933
- /**
6934
- * Get min date based on constraint type
6935
- */
6936
- const getMinDate = (constraint, minDate) => {
6937
- if (minDate) {
6938
- // If it's 'today', return today's date
6939
- if (minDate === 'today') {
6940
- return formatDateToISO(new Date());
6941
- }
6942
- return minDate;
6943
- }
6944
- if (constraint === 'past-only') {
6945
- // No minimum for past-only (can select any past date)
6946
- return undefined;
6947
- }
6948
- if (constraint === 'future-only') {
6949
- // Minimum is today for future-only
6950
- return formatDateToISO(new Date());
6951
- }
6952
- return undefined;
6953
- };
6954
- /**
6955
- * Get max date based on constraint type
6956
- */
6957
- const getMaxDate = (constraint, maxDate) => {
6958
- if (maxDate) {
6959
- // If it's 'today', return today's date
6960
- if (maxDate === 'today') {
6961
- return formatDateToISO(new Date());
6962
- }
6963
- return maxDate;
6964
- }
6965
- if (constraint === 'past-only') {
6966
- // Maximum is today for past-only
6967
- return formatDateToISO(new Date());
6968
- }
6969
- if (constraint === 'future-only') {
6970
- // No maximum for future-only (can select any future date)
6971
- return undefined;
6972
- }
6973
- return undefined;
6974
- };
6975
- /**
6976
- * Validate date constraints
6977
- */
6978
- const validateDateConstraints = (date, minDate, maxDate, constraint) => {
6979
- if (!date)
6980
- return null;
6981
- const dateObj = date instanceof Date ? date : parseDate(date);
6982
- if (!dateObj)
6983
- return null;
6984
- const dateISO = formatDateToISO(dateObj);
6985
- const todayISO = formatDateToISO(new Date());
6986
- // Check constraint type
6987
- if (constraint === 'past-only') {
6988
- if (dateISO > todayISO) {
6989
- return 'Date must be in the past';
6990
- }
6991
- }
6992
- else if (constraint === 'future-only') {
6993
- if (dateISO < todayISO) {
6994
- return 'Date must be in the future';
6995
- }
6996
- }
6997
- // Check minDate
6998
- const effectiveMinDate = getMinDate(constraint, minDate);
6999
- if (effectiveMinDate && dateISO < effectiveMinDate) {
7000
- return `Date must be on or after ${effectiveMinDate}`;
7001
- }
7002
- // Check maxDate
7003
- const effectiveMaxDate = getMaxDate(constraint, maxDate);
7004
- if (effectiveMaxDate && dateISO > effectiveMaxDate) {
7005
- return `Date must be on or before ${effectiveMaxDate}`;
7006
- }
7007
- return null;
7008
- };
7009
-
7010
7011
  const DateInputWidget = ({ config }) => {
7011
7012
  const { value, formattedValue, error, touched, isEnabled, onChange, onBlur, config: widgetConfig, } = useBaseWidget({ config });
7012
7013
  const { translate, translateConfig } = useWidgetTranslation();
@@ -8095,6 +8096,13 @@ const TableCellNumber = ({ config, value, onValueChange }) => {
8095
8096
  };
8096
8097
  return (jsxRuntimeExports.jsx("input", { type: "number", value: displayValue, onChange: handleChange, disabled: isReadonly, placeholder: placeholder, min: validationConfig?.min, max: validationConfig?.max, step: formatConfig?.decimalPlaces ? Math.pow(0.1, formatConfig.decimalPlaces) : undefined, className: `w-full h-[28px] px-2 text-sm border focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 text-right ${isReadonly ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'} border-gray-300`, style: { borderRadius: '10px' } }));
8097
8098
  };
8099
+ const TableCellDate = ({ config, value, onValueChange }) => {
8100
+ const isReadonly = config['widget-readonly'] || false;
8101
+ const placeholder = config['widget-data-placeholder'] || '';
8102
+ // input type="date" requires YYYY-MM-DD format
8103
+ const displayValue = value && typeof value === 'string' ? value.split('T')[0] : '';
8104
+ return (jsxRuntimeExports.jsx("input", { type: "date", value: displayValue, onChange: (e) => onValueChange(e.target.value), disabled: isReadonly, placeholder: placeholder, className: `w-full h-[28px] px-2 text-sm border focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 ${isReadonly ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'} border-gray-300`, style: { borderRadius: '10px' } }));
8105
+ };
8098
8106
  const TableWidget = ({ config }) => {
8099
8107
  const { value, error, touched, isEnabled, onChange, config: widgetConfig, } = useBaseWidget({ config });
8100
8108
  const { translate, translateConfig } = useWidgetTranslation();
@@ -8478,6 +8486,9 @@ const TableWidget = ({ config }) => {
8478
8486
  else if (widgetType === 'number') {
8479
8487
  return jsxRuntimeExports.jsx(TableCellNumber, { config: cellConfig, value: cellValue, onValueChange: (newValue) => updateCellValue(columnKey, newValue, rowIndex) });
8480
8488
  }
8489
+ else if (widgetType === 'date') {
8490
+ return jsxRuntimeExports.jsx(TableCellDate, { config: cellConfig, value: cellValue, onValueChange: (newValue) => updateCellValue(columnKey, newValue, rowIndex) });
8491
+ }
8481
8492
  // For other widget types, use WidgetRenderer but with compact styling
8482
8493
  return (jsxRuntimeExports.jsx("div", { className: "table-cell-widget", style: { margin: 0, padding: 0 }, children: jsxRuntimeExports.jsx(WidgetRenderer, { config: cellConfig, schemaData: {
8483
8494
  [cellWidgetId]: cellValue !== undefined ? cellValue : (column['widget-data-default'] ?? ''),