@xsolla/xui-date-picker 0.225.0 → 0.226.0

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/native/index.mjs CHANGED
@@ -588,6 +588,7 @@ var getFixedPlacementStyle = (side, align, container, panel, viewport, offset) =
588
588
  const vertical = align === "center" ? { top: (container.top + container.bottom) / 2 - panel.height / 2 } : align === "end" ? { bottom: viewport.height - container.bottom } : { top: container.top };
589
589
  return { position: "fixed", ...horizontal, ...vertical };
590
590
  };
591
+ var isSegmentKey = (key) => /^[0-9]$/.test(key) || key === "Backspace" || key === "ArrowUp" || key === "ArrowDown" || key === "ArrowLeft" || key === "ArrowRight";
591
592
  var DatePicker = forwardRef(
592
593
  ({
593
594
  onChange,
@@ -652,6 +653,7 @@ var DatePicker = forwardRef(
652
653
  const [activeSegment, setActiveSegment] = useState(null);
653
654
  const [internalChip, setInternalChip] = useState(null);
654
655
  const [parseError, setParseError] = useState(null);
656
+ const [rawText, setRawText] = useState(null);
655
657
  const [fixedPosition, setFixedPosition] = useState(
656
658
  null
657
659
  );
@@ -728,7 +730,7 @@ var DatePicker = forwardRef(
728
730
  (g) => g.day != null || g.month != null || g.year != null
729
731
  );
730
732
  const showMask = hasAnyFilled || focused && isWeb;
731
- const inputValue = chipLabel ?? (showMask ? composed.text : "");
733
+ const inputValue = rawText ?? chipLabel ?? (showMask ? composed.text : "");
732
734
  const showClear = clearable && editable && (hasAnyFilled || !!chipLabel);
733
735
  const orderedSegments = () => composeField().segments;
734
736
  const firstSegmentKey = () => {
@@ -797,6 +799,60 @@ var DatePicker = forwardRef(
797
799
  commitPart(edit.group, edit.type, Number(edit.buffer));
798
800
  setEdit(null);
799
801
  };
802
+ const parseOne = (token) => {
803
+ const trimmed = token.trim();
804
+ if (!trimmed) return null;
805
+ const parsed = parse(trimmed, resolvedFormat, /* @__PURE__ */ new Date());
806
+ return isValid(parsed) ? parsed : null;
807
+ };
808
+ const parseFieldText = (text) => {
809
+ if (isRange) {
810
+ const [startText, endText] = text.split(RANGE_SEPARATOR);
811
+ const start = parseOne(startText ?? "");
812
+ const end = parseOne(endText ?? "");
813
+ if (!start || endText?.trim() && !end) {
814
+ return {
815
+ error: `Enter a valid date range (${resolvedFormat} - ${resolvedFormat})`
816
+ };
817
+ }
818
+ return { parts: [datePartsFromDate(start), datePartsFromDate(end)] };
819
+ }
820
+ const date = parseOne(text);
821
+ if (!date) return { error: `Enter a valid date (${resolvedFormat})` };
822
+ return { parts: [datePartsFromDate(date)] };
823
+ };
824
+ const isOutOfBounds = (parts) => parts.map(datePartsToDate).some((d) => d instanceof Date && !isWithinBounds(d));
825
+ const commitRawText = (text) => {
826
+ setEdit(null);
827
+ if (!text.trim()) {
828
+ setRawText(null);
829
+ setParseError(null);
830
+ setGroups(propGroups());
831
+ return;
832
+ }
833
+ const result = parseFieldText(text);
834
+ if ("error" in result) {
835
+ setParseError(result.error);
836
+ return;
837
+ }
838
+ if (isOutOfBounds(result.parts)) {
839
+ setParseError("Date must be within the allowed range");
840
+ return;
841
+ }
842
+ setRawText(null);
843
+ setParseError(null);
844
+ setGroups(result.parts);
845
+ emit(result.parts);
846
+ };
847
+ const handleChangeText = (text) => {
848
+ rest.onChangeText?.(text);
849
+ if (!editable) return;
850
+ markDirty();
851
+ setEdit(null);
852
+ setParseError(null);
853
+ setActiveSegment(null);
854
+ setRawText(text);
855
+ };
800
856
  const defaultPart = (type) => {
801
857
  const now = /* @__PURE__ */ new Date();
802
858
  if (type === "day") return now.getDate();
@@ -889,10 +945,12 @@ var DatePicker = forwardRef(
889
945
  }
890
946
  if (!isWeb || !editable) return;
891
947
  if (e.key === "Enter") {
892
- commitBuffer();
948
+ if (rawText !== null) commitRawText(rawText);
949
+ else commitBuffer();
893
950
  setOpen(false);
894
951
  return;
895
952
  }
953
+ if (rawText !== null && isSegmentKey(e.key)) setRawText(null);
896
954
  if (e.key === "ArrowUp") return handleArrowStep(e, 1);
897
955
  if (e.key === "ArrowDown") return handleArrowStep(e, -1);
898
956
  if (e.key === "ArrowLeft") return handleArrowMove(e, -1);
@@ -945,30 +1003,33 @@ var DatePicker = forwardRef(
945
1003
  setParseError(null);
946
1004
  return;
947
1005
  }
1006
+ if (rawText !== null) {
1007
+ commitRawText(rawText);
1008
+ return;
1009
+ }
948
1010
  let next = groups;
1011
+ let rejected = false;
949
1012
  if (edit) {
950
1013
  const clamped = clampDatePart(
951
1014
  edit.type,
952
1015
  Number(edit.buffer),
953
1016
  groups[edit.group]
954
1017
  );
955
- next = groups.map(
956
- (g, i) => i === edit.group ? { ...g, [edit.type]: clamped } : g
957
- );
1018
+ const edited = { ...groups[edit.group], [edit.type]: clamped };
1019
+ const assembled = datePartsToDate(edited);
1020
+ if (assembled && !isWithinBounds(assembled)) {
1021
+ rejected = true;
1022
+ } else {
1023
+ next = groups.map((g, i) => i === edit.group ? edited : g);
1024
+ }
958
1025
  }
959
1026
  const props = propGroups();
960
1027
  next = next.map((g, i) => isDatePartsComplete(g) ? g : props[i]);
961
1028
  setEdit(null);
962
- setParseError(null);
1029
+ setParseError(rejected ? "Date must be within the allowed range" : null);
963
1030
  setGroups(next);
964
1031
  emit(next);
965
1032
  };
966
- const parseOne = (token) => {
967
- const trimmed = token.trim();
968
- if (!trimmed) return null;
969
- const parsed = parse(trimmed, resolvedFormat, /* @__PURE__ */ new Date());
970
- return isValid(parsed) ? parsed : null;
971
- };
972
1033
  const handlePaste = (e) => {
973
1034
  onPasteProp?.(e);
974
1035
  if (e.defaultPrevented) return;
@@ -976,34 +1037,20 @@ var DatePicker = forwardRef(
976
1037
  const text = e.clipboardData?.getData("text") ?? "";
977
1038
  if (!text.trim()) return;
978
1039
  e.preventDefault();
979
- let next;
980
- if (isRange) {
981
- const [startText, endText] = text.split(RANGE_SEPARATOR);
982
- const start = parseOne(startText ?? "");
983
- const end = parseOne(endText ?? "");
984
- if (!start) {
985
- setParseError(
986
- `Enter a valid date range (${resolvedFormat} - ${resolvedFormat})`
987
- );
988
- return;
989
- }
990
- next = [datePartsFromDate(start), datePartsFromDate(end)];
991
- } else {
992
- const date = parseOne(text);
993
- if (!date) {
994
- setParseError(`Enter a valid date (${resolvedFormat})`);
995
- return;
996
- }
997
- next = [datePartsFromDate(date)];
1040
+ const result = parseFieldText(text);
1041
+ if ("error" in result) {
1042
+ setParseError(result.error);
1043
+ return;
998
1044
  }
999
- const outOfBounds = next.map(datePartsToDate).some((d) => d instanceof Date && !isWithinBounds(d));
1000
- if (outOfBounds) {
1045
+ const next = result.parts;
1046
+ if (isOutOfBounds(next)) {
1001
1047
  setParseError("Date must be within the allowed range");
1002
1048
  return;
1003
1049
  }
1004
1050
  markDirty();
1005
1051
  setParseError(null);
1006
1052
  setEdit(null);
1053
+ setRawText(null);
1007
1054
  setGroups(next);
1008
1055
  emit(next);
1009
1056
  };
@@ -1018,6 +1065,7 @@ var DatePicker = forwardRef(
1018
1065
  sessionRef.current.cleared = true;
1019
1066
  setGroups(next);
1020
1067
  setEdit(null);
1068
+ setRawText(null);
1021
1069
  setParseError(null);
1022
1070
  setInternalChip(null);
1023
1071
  onChipSelect?.(null);
@@ -1037,6 +1085,7 @@ var DatePicker = forwardRef(
1037
1085
  sessionRef.current = { dirty: false, cleared: false, propKey };
1038
1086
  setGroups(propGroups());
1039
1087
  setEdit(null);
1088
+ setRawText(null);
1040
1089
  setParseError(null);
1041
1090
  }, [propKey]);
1042
1091
  useEffect(() => {
@@ -1234,6 +1283,7 @@ var DatePicker = forwardRef(
1234
1283
  onKeyDown: handleKeyDown,
1235
1284
  onClick: handleFieldClick,
1236
1285
  onPaste: handlePaste,
1286
+ onChangeText: handleChangeText,
1237
1287
  iconRight,
1238
1288
  iconLeft,
1239
1289
  extraClear: showClear,