@xsolla/xui-date-picker 0.218.0 → 0.219.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/web/index.mjs CHANGED
@@ -66,6 +66,10 @@ var ADDITIONAL_BLOCKED_PROPS = /* @__PURE__ */ new Set([
66
66
  "fontWeight",
67
67
  "fontFamily",
68
68
  "textDecoration",
69
+ // `display` is a presentation attribute on SVG, so isPropValid lets it
70
+ // through — but Box consumes it purely as CSS. Without this it lands on the
71
+ // DOM node as a bogus `display="inline-flex"` attribute.
72
+ "display",
69
73
  // Cross-platform layout prop that Text consumes as CSS, never as an
70
74
  // attribute. Pinned explicitly because it is now spread into the styled
71
75
  // component rather than destructured away before it can reach the DOM.
@@ -100,7 +104,7 @@ function createFilteredElement(defaultTag) {
100
104
  import { jsx } from "react/jsx-runtime";
101
105
  var FilteredDiv = createFilteredElement("div");
102
106
  var StyledBox = styled(FilteredDiv)`
103
- display: flex;
107
+ display: ${(props) => props.display || "flex"};
104
108
  box-sizing: border-box;
105
109
  background-color: ${(props) => props.backgroundColor || "transparent"};
106
110
  border-color: ${(props) => props.borderColor || "transparent"};
@@ -236,7 +240,7 @@ var Box = React2.forwardRef(
236
240
  onLoad,
237
241
  "data-testid": dataTestId || testID,
238
242
  style: {
239
- display: "block",
243
+ display: props.display || "block",
240
244
  objectFit: "cover",
241
245
  width: typeof props.width === "number" ? `${props.width}px` : props.width,
242
246
  height: typeof props.height === "number" ? `${props.height}px` : props.height,
@@ -636,6 +640,9 @@ var DatePicker = forwardRef(
636
640
  overlayThemeProductContext,
637
641
  iconRight = /* @__PURE__ */ jsx2(CalendarFieldIcon, {}),
638
642
  iconLeft,
643
+ label,
644
+ clearable = false,
645
+ onClear,
639
646
  error,
640
647
  errorMessage,
641
648
  readOnly,
@@ -657,6 +664,7 @@ var DatePicker = forwardRef(
657
664
  () => isRange ? [datePartsFromDate(startDate), datePartsFromDate(endDate)] : [datePartsFromDate(selectedDate)],
658
665
  [isRange, startDate, endDate, selectedDate]
659
666
  );
667
+ const propKey = `${selectedDate?.getTime() ?? ""}|${startDate?.getTime() ?? ""}|${endDate?.getTime() ?? ""}|${isRange}`;
660
668
  const [open, setOpen] = useState(false);
661
669
  const [groups, setGroups] = useState(propGroups);
662
670
  const [edit, setEdit] = useState(null);
@@ -672,6 +680,15 @@ var DatePicker = forwardRef(
672
680
  const inputRef = useRef(null);
673
681
  const chipSelectionRef = useRef(false);
674
682
  const suppressReopenRef = useRef(false);
683
+ const sessionRef = useRef({
684
+ dirty: false,
685
+ cleared: false,
686
+ propKey
687
+ });
688
+ const markDirty = () => {
689
+ sessionRef.current.dirty = true;
690
+ sessionRef.current.cleared = false;
691
+ };
675
692
  const activeChipValue = activeChip !== void 0 ? activeChip : internalChip;
676
693
  const chipLabel = showChipLabel && activeChipValue && chips ? chips.find((c) => c.value === activeChipValue)?.label : void 0;
677
694
  const isWithinBounds = (date) => {
@@ -731,6 +748,7 @@ var DatePicker = forwardRef(
731
748
  );
732
749
  const showMask = hasAnyFilled || focused && isWeb;
733
750
  const inputValue = chipLabel ?? (showMask ? composed.text : "");
751
+ const showClear = clearable && editable && (hasAnyFilled || !!chipLabel);
734
752
  const orderedSegments = () => composeField().segments;
735
753
  const firstSegmentKey = () => {
736
754
  const first = orderedSegments()[0];
@@ -773,6 +791,7 @@ var DatePicker = forwardRef(
773
791
  if (start || end) applySelection([start, end], false);
774
792
  };
775
793
  const commitPart = (group, type, rawValue) => {
794
+ markDirty();
776
795
  const clamped = clampDatePart(type, rawValue, groups[group]);
777
796
  const next = groups.map((g, i) => {
778
797
  if (i !== group) return g;
@@ -821,6 +840,7 @@ var DatePicker = forwardRef(
821
840
  setEdit(null);
822
841
  if (committed !== null) advance(active);
823
842
  } else {
843
+ markDirty();
824
844
  setEdit({ group: active.group, type: active.type, buffer });
825
845
  }
826
846
  };
@@ -858,6 +878,7 @@ var DatePicker = forwardRef(
858
878
  e.preventDefault();
859
879
  const active = activeSegment ?? firstSegmentKey();
860
880
  if (!active) return;
881
+ markDirty();
861
882
  const sameActive = edit && edit.group === active.group && edit.type === active.type;
862
883
  if (sameActive && edit.buffer.length > 0) {
863
884
  const buffer = edit.buffer.slice(0, -1);
@@ -909,6 +930,11 @@ var DatePicker = forwardRef(
909
930
  suppressReopenRef.current = false;
910
931
  return;
911
932
  }
933
+ sessionRef.current = {
934
+ dirty: false,
935
+ cleared: sessionRef.current.cleared,
936
+ propKey
937
+ };
912
938
  setFocused(true);
913
939
  setOpen(true);
914
940
  if (isWeb && editable) setActiveSegment(firstSegmentKey());
@@ -925,7 +951,19 @@ var DatePicker = forwardRef(
925
951
  onBlurProp?.(e);
926
952
  setFocused(false);
927
953
  setActiveSegment(null);
954
+ if (sessionRef.current.propKey !== propKey && !sessionRef.current.dirty) {
955
+ sessionRef.current = { dirty: false, cleared: false, propKey };
956
+ setEdit(null);
957
+ setParseError(null);
958
+ setGroups(propGroups());
959
+ return;
960
+ }
928
961
  if (!editable) return;
962
+ if (sessionRef.current.cleared && !sessionRef.current.dirty) {
963
+ setEdit(null);
964
+ setParseError(null);
965
+ return;
966
+ }
929
967
  let next = groups;
930
968
  if (edit) {
931
969
  const clamped = clampDatePart(
@@ -982,6 +1020,7 @@ var DatePicker = forwardRef(
982
1020
  setParseError("Date must be within the allowed range");
983
1021
  return;
984
1022
  }
1023
+ markDirty();
985
1024
  setParseError(null);
986
1025
  setEdit(null);
987
1026
  setGroups(next);
@@ -992,13 +1031,29 @@ var DatePicker = forwardRef(
992
1031
  setInternalChip(value);
993
1032
  onChipSelect?.(value);
994
1033
  };
1034
+ const handleClear = () => {
1035
+ const next = isRange ? [datePartsFromDate(null), datePartsFromDate(null)] : [datePartsFromDate(null)];
1036
+ sessionRef.current.dirty = false;
1037
+ sessionRef.current.cleared = true;
1038
+ setGroups(next);
1039
+ setEdit(null);
1040
+ setParseError(null);
1041
+ setInternalChip(null);
1042
+ onChipSelect?.(null);
1043
+ if (isWeb && editable) setActiveSegment(firstSegmentKey());
1044
+ onClear?.();
1045
+ if (isRange) {
1046
+ const emptyRange = [null, null];
1047
+ onChange?.(emptyRange);
1048
+ }
1049
+ };
995
1050
  const handleDateChange = (date) => applySelection(date, closeOnComplete);
996
1051
  const closePanel = useCallback(() => setOpen(false), []);
997
1052
  const renderTopContent = topContent ? () => topContent({ close: closePanel }) : void 0;
998
1053
  const renderBottomContent = bottomContent ? () => bottomContent({ close: closePanel }) : void 0;
999
- const propKey = `${selectedDate?.getTime() ?? ""}|${startDate?.getTime() ?? ""}|${endDate?.getTime() ?? ""}|${isRange}`;
1000
1054
  useEffect(() => {
1001
1055
  if (focused) return;
1056
+ sessionRef.current = { dirty: false, cleared: false, propKey };
1002
1057
  setGroups(propGroups());
1003
1058
  setEdit(null);
1004
1059
  setParseError(null);
@@ -1175,6 +1230,7 @@ var DatePicker = forwardRef(
1175
1230
  ...rest,
1176
1231
  ref: inputRef,
1177
1232
  size,
1233
+ label,
1178
1234
  placeholder: placeholder || (isRange ? "Choose a date range" : "Choose a date"),
1179
1235
  value: inputValue,
1180
1236
  readOnly,
@@ -1185,6 +1241,8 @@ var DatePicker = forwardRef(
1185
1241
  onPaste: handlePaste,
1186
1242
  iconRight,
1187
1243
  iconLeft,
1244
+ extraClear: showClear,
1245
+ onRemove: handleClear,
1188
1246
  error: error || !!parseError,
1189
1247
  errorMessage: parseError ?? errorMessage,
1190
1248
  backgroundColor,