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