@sustaina/shared-ui 1.70.6 → 1.70.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.mjs CHANGED
@@ -15,7 +15,7 @@ import { createPortal } from 'react-dom';
15
15
  import * as SelectPrimitive from '@radix-ui/react-select';
16
16
  import { useForm, FormProvider, Controller, useFormContext, useFormState, useFieldArray, useWatch } from 'react-hook-form';
17
17
  import * as LabelPrimitive from '@radix-ui/react-label';
18
- import { format, isValid, parseISO, isAfter, compareAsc, parse } from 'date-fns';
18
+ import { format, isValid, parseISO, compareAsc, parse, isAfter } from 'date-fns';
19
19
  import * as PopoverPrimitive from '@radix-ui/react-popover';
20
20
  import { Command as Command$1 } from 'cmdk';
21
21
  import * as DialogPrimitive2 from '@radix-ui/react-dialog';
@@ -5500,6 +5500,77 @@ var DatePicker2 = (props) => {
5500
5500
  // src/components/advanceSearch/components/constants/index.ts
5501
5501
  var fallbackShortDateFormat = "DD/MM/YYYY";
5502
5502
  var UUIDregex = /^[0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$/i;
5503
+ var RANGE_START_ERROR = "Start value must be before end value.";
5504
+ var RANGE_END_ERROR = "End value must be after start value.";
5505
+ var RANGE_VALIDATED_TYPES = ["date", "datetime", "datemonth"];
5506
+ var sanitizeInput = (val) => {
5507
+ if (!val) return val;
5508
+ if (Array.isArray(val)) {
5509
+ return val.map((v) => sanitizeInput(v));
5510
+ }
5511
+ if (val.includes("\n") || val.includes("\r") || /[\u2028\u2029]/u.test(val))
5512
+ return "__INVALID_NEWLINE__";
5513
+ if (/\\\\/.test(val)) return "__INVALID_ESCAPE__";
5514
+ if (/\\(n|t|r|b|f|u[0-9a-fA-F]{4})/.test(val)) return "__INVALID_ESCAPE__";
5515
+ if (/\p{Cf}/u.test(val)) return "__INVALID_UNICODE_WHITESPACE__";
5516
+ if (/[\u00A0\u1680\u180E\u202F\u205F\u3000]/u.test(val)) return "__INVALID_UNICODE_WHITESPACE__";
5517
+ const trimmed = val.trim();
5518
+ if (/^\{.*\}$/s.test(trimmed) || /^\[.*\]$/s.test(trimmed)) return "__INVALID_JSON_LITERAL__";
5519
+ if (/\\\{/.test(val) || /\\\}/.test(val)) return "__INVALID_JSON_ESCAPE__";
5520
+ if (/[%*~^]/.test(trimmed)) return "__INVALID_WILDCARD__";
5521
+ if (/[%><={}\\[\]"']/u.test(trimmed)) return "__INVALID_CHAR__";
5522
+ if (/\p{Cc}/u.test(val)) return "__INVALID_CONTROL_CHAR__";
5523
+ return val;
5524
+ };
5525
+ var numericTypes = ["number"];
5526
+ var dateTypes = ["date", "datemonth"];
5527
+ var validateByFieldType = (value, fieldType) => {
5528
+ if (!value) return { valid: true };
5529
+ if (Array.isArray(value)) {
5530
+ return { valid: true };
5531
+ }
5532
+ if (numericTypes.includes(fieldType)) {
5533
+ if (!/^-?\d+(\.\d+)?$/.test(value)) {
5534
+ return { valid: false, message: "Please enter a valid number." };
5535
+ }
5536
+ }
5537
+ if (fieldType === "uuid") {
5538
+ if (!UUIDregex.test(value)) {
5539
+ return { valid: false, message: "Please enter a valid UUID." };
5540
+ }
5541
+ }
5542
+ if (dateTypes.includes(fieldType)) {
5543
+ const normalized = fieldType === "datemonth" ? `${value}-01` : value;
5544
+ const parsed = parseISO(normalized);
5545
+ if (!isValid(parsed)) {
5546
+ return { valid: false, message: "Invalid date format." };
5547
+ }
5548
+ }
5549
+ return { valid: true };
5550
+ };
5551
+ var parseRangeValue = (raw, fieldType) => {
5552
+ if (!raw) return void 0;
5553
+ const normalized = fieldType === "datemonth" ? `${raw}-01` : raw;
5554
+ const parsed = parseISO(normalized);
5555
+ return isValid(parsed) ? parsed : void 0;
5556
+ };
5557
+ var validateDateRange = (which, row, formValues) => {
5558
+ if (row.operator !== "between" || !RANGE_VALIDATED_TYPES.includes(row.fieldType)) return true;
5559
+ const start = formValues[`value_${row.id}`];
5560
+ const end = formValues[`value2_${row.id}`];
5561
+ if (typeof start !== "string" || typeof end !== "string" || !start || !end) return true;
5562
+ const d1 = parseRangeValue(start, row.fieldType);
5563
+ const d2 = parseRangeValue(end, row.fieldType);
5564
+ if (d1 && d2 && isAfter(d1, d2)) {
5565
+ return which === "value" ? RANGE_START_ERROR : RANGE_END_ERROR;
5566
+ }
5567
+ return true;
5568
+ };
5569
+ var buildValueRules = (which, row) => ({
5570
+ required: "This field is required.",
5571
+ validate: (_value, formValues) => validateDateRange(which, row, formValues),
5572
+ deps: [which === "value" ? `value2_${row.id}` : `value_${row.id}`]
5573
+ });
5503
5574
  var ConditionDateInput = ({
5504
5575
  row,
5505
5576
  control,
@@ -5523,8 +5594,9 @@ var ConditionDateInput = ({
5523
5594
  {
5524
5595
  control,
5525
5596
  name: fieldName,
5526
- rules: { required: "This field is required." },
5597
+ rules: buildValueRules(which, row),
5527
5598
  render: ({ field, fieldState }) => {
5599
+ const { value: fieldValue, ...fieldRest } = field;
5528
5600
  const handleValueChange = (next) => {
5529
5601
  field.onChange(next ?? void 0);
5530
5602
  if (!next) {
@@ -5535,8 +5607,8 @@ var ConditionDateInput = ({
5535
5607
  /* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
5536
5608
  DatePicker2,
5537
5609
  {
5538
- ...field,
5539
- value: field.value || void 0,
5610
+ ...fieldRest,
5611
+ value: fieldValue || void 0,
5540
5612
  onValueChange: handleValueChange,
5541
5613
  placeholder: dateFormat,
5542
5614
  ariaLabel: buildAriaLabel(options?.isEnd),
@@ -5656,12 +5728,9 @@ function MonthCal({
5656
5728
  );
5657
5729
  const min = React.useMemo(() => normalizeMonth(minDate), [minDate]);
5658
5730
  const max = React.useMemo(() => normalizeMonth(maxDate), [maxDate]);
5659
- let effectiveMin = min;
5660
- if (min && max && min > max) {
5661
- effectiveMin = max;
5662
- }
5663
- const minYear = effectiveMin?.getFullYear();
5664
- const minMonth = effectiveMin?.getMonth();
5731
+ const hasConflictingBounds = Boolean(min && max && min > max);
5732
+ const minYear = min?.getFullYear();
5733
+ const minMonth = min?.getMonth();
5665
5734
  const maxYear = max?.getFullYear();
5666
5735
  const maxMonth = max?.getMonth();
5667
5736
  const selectedMonthYear = selectedMonthDate?.getFullYear();
@@ -5674,6 +5743,7 @@ function MonthCal({
5674
5743
  }
5675
5744
  }, [selectedMonthYear]);
5676
5745
  React.useEffect(() => {
5746
+ if (hasConflictingBounds) return;
5677
5747
  if (typeof minYear === "number" && menuYear < minYear) {
5678
5748
  setMenuYear(minYear);
5679
5749
  return;
@@ -5681,10 +5751,11 @@ function MonthCal({
5681
5751
  if (typeof maxYear === "number" && menuYear > maxYear) {
5682
5752
  setMenuYear(maxYear);
5683
5753
  }
5684
- }, [minYear, maxYear, menuYear]);
5685
- const disablePrevYear = typeof minYear === "number" ? menuYear <= minYear : false;
5686
- const disableNextYear = typeof maxYear === "number" ? menuYear >= maxYear : false;
5754
+ }, [hasConflictingBounds, minYear, maxYear, menuYear]);
5755
+ const disablePrevYear = hasConflictingBounds || (typeof minYear === "number" ? menuYear <= minYear : false);
5756
+ const disableNextYear = hasConflictingBounds || (typeof maxYear === "number" ? menuYear >= maxYear : false);
5687
5757
  const yearOptions = React.useMemo(() => {
5758
+ if (hasConflictingBounds) return [menuYear];
5688
5759
  const fallbackWindow = 50;
5689
5760
  const start = typeof minYear === "number" ? minYear : menuYear - fallbackWindow;
5690
5761
  const end = typeof maxYear === "number" ? maxYear : menuYear + fallbackWindow;
@@ -5697,7 +5768,7 @@ function MonthCal({
5697
5768
  years.sort((a, b) => a - b);
5698
5769
  }
5699
5770
  return years;
5700
- }, [maxYear, menuYear, minYear]);
5771
+ }, [hasConflictingBounds, maxYear, menuYear, minYear]);
5701
5772
  const formatYearLabel = React.useCallback(
5702
5773
  (year) => {
5703
5774
  const raw = callbacks?.yearLabel?.(year);
@@ -5708,13 +5779,14 @@ function MonthCal({
5708
5779
  );
5709
5780
  const handleYearSelect = React.useCallback(
5710
5781
  (nextValue) => {
5782
+ if (hasConflictingBounds) return;
5711
5783
  const nextYear = Number.parseInt(nextValue, 10);
5712
5784
  if (Number.isNaN(nextYear)) return;
5713
5785
  if (typeof minYear === "number" && nextYear < minYear) return;
5714
5786
  if (typeof maxYear === "number" && nextYear > maxYear) return;
5715
5787
  setMenuYear(nextYear);
5716
5788
  },
5717
- [maxYear, minYear]
5789
+ [hasConflictingBounds, maxYear, minYear]
5718
5790
  );
5719
5791
  const disabledPairs = React.useMemo(() => {
5720
5792
  if (!disabledDates?.length) return [];
@@ -5799,7 +5871,7 @@ function MonthCal({
5799
5871
  const disabledByList = disabledPairs.some(
5800
5872
  (d) => d.year === menuYear && d.month === m.number
5801
5873
  );
5802
- const isDisabled = afterMax || beforeMin || disabledByList;
5874
+ const isDisabled = hasConflictingBounds || afterMax || beforeMin || disabledByList;
5803
5875
  const cellDate = new Date(menuYear, m.number, 1);
5804
5876
  cellDate.setHours(0, 0, 0, 0);
5805
5877
  const isSelected = !isDisabled && !!selectedMonthDate && selectedMonthDate.getFullYear() === menuYear && selectedMonthDate.getMonth() === m.number;
@@ -6045,9 +6117,10 @@ var ConditionMonthInput = ({ row, control, onClear }) => {
6045
6117
  {
6046
6118
  control,
6047
6119
  name: fieldName,
6048
- rules: { required: "This field is required." },
6120
+ rules: buildValueRules(which, row),
6049
6121
  render: ({ field, fieldState }) => {
6050
- const value = field.value || void 0;
6122
+ const { value: fieldValue, ...fieldRest } = field;
6123
+ const value = fieldValue || void 0;
6051
6124
  const handleValueChange = (next) => {
6052
6125
  field.onChange(next ?? void 0);
6053
6126
  if (!next) {
@@ -6058,7 +6131,7 @@ var ConditionMonthInput = ({ row, control, onClear }) => {
6058
6131
  /* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
6059
6132
  MonthPicker2,
6060
6133
  {
6061
- ...field,
6134
+ ...fieldRest,
6062
6135
  value,
6063
6136
  onValueChange: handleValueChange,
6064
6137
  placeholder,
@@ -8605,51 +8678,6 @@ function transformFilterKeys(obj, fieldMap = FILTER_FIELD_MAP) {
8605
8678
  }
8606
8679
  return obj;
8607
8680
  }
8608
- var sanitizeInput = (val) => {
8609
- if (!val) return val;
8610
- if (Array.isArray(val)) {
8611
- return val.map((v) => sanitizeInput(v));
8612
- }
8613
- if (val.includes("\n") || val.includes("\r") || /[\u2028\u2029]/u.test(val))
8614
- return "__INVALID_NEWLINE__";
8615
- if (/\\\\/.test(val)) return "__INVALID_ESCAPE__";
8616
- if (/\\(n|t|r|b|f|u[0-9a-fA-F]{4})/.test(val)) return "__INVALID_ESCAPE__";
8617
- if (/\p{Cf}/u.test(val)) return "__INVALID_UNICODE_WHITESPACE__";
8618
- if (/[\u00A0\u1680\u180E\u202F\u205F\u3000]/u.test(val)) return "__INVALID_UNICODE_WHITESPACE__";
8619
- const trimmed = val.trim();
8620
- if (/^\{.*\}$/s.test(trimmed) || /^\[.*\]$/s.test(trimmed)) return "__INVALID_JSON_LITERAL__";
8621
- if (/\\\{/.test(val) || /\\\}/.test(val)) return "__INVALID_JSON_ESCAPE__";
8622
- if (/[%*~^]/.test(trimmed)) return "__INVALID_WILDCARD__";
8623
- if (/[%><={}\\[\]"']/u.test(trimmed)) return "__INVALID_CHAR__";
8624
- if (/\p{Cc}/u.test(val)) return "__INVALID_CONTROL_CHAR__";
8625
- return val;
8626
- };
8627
- var numericTypes = ["number"];
8628
- var dateTypes = ["date", "datemonth"];
8629
- var validateByFieldType = (value, fieldType) => {
8630
- if (!value) return { valid: true };
8631
- if (Array.isArray(value)) {
8632
- return { valid: true };
8633
- }
8634
- if (numericTypes.includes(fieldType)) {
8635
- if (!/^-?\d+(\.\d+)?$/.test(value)) {
8636
- return { valid: false, message: "Please enter a valid number." };
8637
- }
8638
- }
8639
- if (fieldType === "uuid") {
8640
- if (!UUIDregex.test(value)) {
8641
- return { valid: false, message: "Please enter a valid UUID." };
8642
- }
8643
- }
8644
- if (dateTypes.includes(fieldType)) {
8645
- const normalized = fieldType === "datemonth" ? `${value}-01` : value;
8646
- const parsed = parseISO(normalized);
8647
- if (!isValid(parsed)) {
8648
- return { valid: false, message: "Invalid date format." };
8649
- }
8650
- }
8651
- return { valid: true };
8652
- };
8653
8681
  var AdvanceSearch = ({
8654
8682
  fields,
8655
8683
  portalId,
@@ -8677,7 +8705,7 @@ var AdvanceSearch = ({
8677
8705
  } = useAdvanceSearch({ fields: fieldsData, limitRows });
8678
8706
  const form = useForm({
8679
8707
  mode: "onSubmit",
8680
- reValidateMode: "onSubmit",
8708
+ reValidateMode: "onChange",
8681
8709
  defaultValues: {}
8682
8710
  });
8683
8711
  const { handleSubmit, unregister, resetField, getValues, clearErrors, setError } = form;
@@ -8702,13 +8730,6 @@ var AdvanceSearch = ({
8702
8730
  },
8703
8731
  [resetField, clearErrors]
8704
8732
  );
8705
- const parseRangeValue = useCallback((raw, fieldType) => {
8706
- if (!raw) return void 0;
8707
- if (Array.isArray(raw)) return void 0;
8708
- const normalized = fieldType === "datemonth" ? `${raw}-01` : raw;
8709
- const parsed = parseISO(normalized);
8710
- return isValid(parsed) ? parsed : void 0;
8711
- }, []);
8712
8733
  const onSubmit = useCallback(() => {
8713
8734
  const operatorValidation = {};
8714
8735
  rows.forEach((r) => {
@@ -8752,16 +8773,6 @@ var AdvanceSearch = ({
8752
8773
  setError(endField, { type: "validate", message: valid2.message });
8753
8774
  return null;
8754
8775
  }
8755
- if (v1 && v2 && ["date", "datemonth"].includes(r.fieldType)) {
8756
- const d1 = parseRangeValue(v1, r.fieldType);
8757
- const d2 = parseRangeValue(v2, r.fieldType);
8758
- if (d1 && d2 && isAfter(d1, d2)) {
8759
- hasError = true;
8760
- setError(startField, { type: "validate", message: "Start value must be before end value." });
8761
- setError(endField, { type: "validate", message: "End value must be after start value." });
8762
- return null;
8763
- }
8764
- }
8765
8776
  return { ...r, value: v1, value2: v2 };
8766
8777
  }
8767
8778
  return { ...r, value: v1 };
@@ -8791,7 +8802,6 @@ var AdvanceSearch = ({
8791
8802
  rows,
8792
8803
  operatorsForField,
8793
8804
  getValues,
8794
- parseRangeValue,
8795
8805
  setError,
8796
8806
  setOperatorErrors,
8797
8807
  filterFieldMap,
@@ -13831,8 +13841,8 @@ var GridSettingsModal = ({
13831
13841
  }
13832
13842
  }, [isOpen, currentColumns, form]);
13833
13843
  const addColumn = async () => {
13834
- const isValid7 = await trigger("columns");
13835
- if (isValid7) {
13844
+ const isValid6 = await trigger("columns");
13845
+ if (isValid6) {
13836
13846
  append({ id: "" });
13837
13847
  requestAnimationFrame(() => {
13838
13848
  const container = scrollRef.current;