@aviala-design/spiral 1.0.0 → 2.0.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/dist/index.cjs CHANGED
@@ -4276,7 +4276,7 @@ function ColorPickerInputs({
4276
4276
  }
4277
4277
  ) : null,
4278
4278
  format === "hex" ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "aviala-color-picker-field", children: [
4279
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "aviala-color-picker-field__badge-area", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { children: "#" }) }),
4279
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "aviala-color-picker-field__badge-area", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { style: "normal", children: "#" }) }),
4280
4280
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4281
4281
  "input",
4282
4282
  {
@@ -4315,7 +4315,7 @@ function ColorPickerInputs({
4315
4315
  onKeyDown: (e) => e.key === "Enter" && commitAlpha()
4316
4316
  }
4317
4317
  ),
4318
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "aviala-color-picker-field__badge-area", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { children: "%" }) })
4318
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "aviala-color-picker-field__badge-area", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { style: "normal", children: "%" }) })
4319
4319
  ] }),
4320
4320
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
4321
4321
  Select,
@@ -4835,6 +4835,7 @@ function Cascader({
4835
4835
  const isValueControlled = valueProp !== void 0;
4836
4836
  const open = isOpenControlled ? openProp : internalOpen;
4837
4837
  const selectedPath = isValueControlled ? valueProp ?? [] : internalValue;
4838
+ const wasOpenRef = (0, import_react35.useRef)(open);
4838
4839
  (0, import_react35.useEffect)(() => {
4839
4840
  const markWindowBlur = () => {
4840
4841
  windowBlurCloseRef.current = true;
@@ -4843,10 +4844,20 @@ function Cascader({
4843
4844
  return () => window.removeEventListener("blur", markWindowBlur, true);
4844
4845
  }, []);
4845
4846
  (0, import_react35.useEffect)(() => {
4846
- if (open) {
4847
- setActivePath(selectedPath.length > 0 ? selectedPath.slice(0, -1) : []);
4847
+ const justOpened = open && !wasOpenRef.current;
4848
+ wasOpenRef.current = open;
4849
+ if (!justOpened) return;
4850
+ if (selectedPath.length === 0) {
4851
+ setActivePath([]);
4852
+ return;
4853
+ }
4854
+ const selectedOption = findOptionPath(options, selectedPath);
4855
+ if (selectedOption?.children?.length) {
4856
+ setActivePath(selectedPath);
4857
+ return;
4848
4858
  }
4849
- }, [open, selectedPath]);
4859
+ setActivePath(selectedPath.slice(0, -1));
4860
+ }, [open, options, selectedPath]);
4850
4861
  (0, import_react35.useEffect)(() => {
4851
4862
  if (!open) {
4852
4863
  pointerDownCloseRef.current = false;
@@ -6120,12 +6131,19 @@ function DatePicker(props) {
6120
6131
  setActivePanel("date");
6121
6132
  return;
6122
6133
  }
6134
+ setFocusedDay((current) => {
6135
+ if (current) return current;
6136
+ if (mode === "single" && singleValue) return startOfDay(singleValue);
6137
+ const rangeAnchor = mode === "range" ? rangeValue.from ?? rangeValue.to : void 0;
6138
+ if (rangeAnchor) return startOfDay(rangeAnchor);
6139
+ return startOfDay(/* @__PURE__ */ new Date());
6140
+ });
6123
6141
  const markPointerDown = () => {
6124
6142
  pointerDownCloseRef.current = true;
6125
6143
  };
6126
6144
  document.addEventListener("pointerdown", markPointerDown, true);
6127
6145
  return () => document.removeEventListener("pointerdown", markPointerDown, true);
6128
- }, [open]);
6146
+ }, [mode, open, rangeValue.from, rangeValue.to, singleValue]);
6129
6147
  const handleOpenChange = (0, import_react39.useCallback)(
6130
6148
  (nextOpen) => {
6131
6149
  if (disabled && nextOpen) return;
@@ -6226,19 +6244,21 @@ function DatePicker(props) {
6226
6244
  ]
6227
6245
  );
6228
6246
  const selectDate = (0, import_react39.useCallback)(
6229
- (date) => {
6247
+ (date, options) => {
6230
6248
  if (disabled || isDateDisabled(date, minDate, maxDate)) return;
6231
6249
  const day = startOfDay(date);
6232
6250
  setFocusedDay(day);
6233
6251
  if (!isSameMonth(day, viewMonth)) {
6234
6252
  setViewMonth(new Date(day.getFullYear(), day.getMonth(), 1));
6235
6253
  }
6254
+ const switchToTime = options?.switchToTime ?? enableTime;
6255
+ const shouldClose = options?.close ?? !enableTime;
6236
6256
  if (mode === "single") {
6237
6257
  const next = enableTime ? applyTimeToDate(day, timeValue.hours, timeValue.minutes) : day;
6238
6258
  commitSingle(next);
6239
- if (enableTime) {
6259
+ if (switchToTime) {
6240
6260
  setActivePanel("time");
6241
- } else {
6261
+ } else if (shouldClose) {
6242
6262
  handleOpenChange(false);
6243
6263
  }
6244
6264
  return;
@@ -6254,9 +6274,9 @@ function DatePicker(props) {
6254
6274
  if (isSameDay(day, currentFrom)) {
6255
6275
  commitRange({ from: withTime(day), to: withTime(day) });
6256
6276
  setRangeDraftFrom(void 0);
6257
- if (enableTime) {
6277
+ if (switchToTime) {
6258
6278
  setActivePanel("time");
6259
- } else {
6279
+ } else if (shouldClose) {
6260
6280
  handleOpenChange(false);
6261
6281
  }
6262
6282
  return;
@@ -6267,9 +6287,9 @@ function DatePicker(props) {
6267
6287
  to: normalized.to ? withTime(normalized.to) : void 0
6268
6288
  });
6269
6289
  setRangeDraftFrom(void 0);
6270
- if (enableTime) {
6290
+ if (switchToTime) {
6271
6291
  setActivePanel("time");
6272
- } else {
6292
+ } else if (shouldClose) {
6273
6293
  handleOpenChange(false);
6274
6294
  }
6275
6295
  },
@@ -6675,7 +6695,7 @@ function DatePickerPanelFooter() {
6675
6695
  if (!enableTime) return null;
6676
6696
  const panelValue = activePanel === "time" ? "time" : "date";
6677
6697
  const isRangeMode = mode === "range";
6678
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
6698
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "aviala-datepicker-footer-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
6679
6699
  SegmentatorGroup,
6680
6700
  {
6681
6701
  value: panelValue,
@@ -6706,7 +6726,7 @@ function DatePickerPanelFooter() {
6706
6726
  )
6707
6727
  ]
6708
6728
  }
6709
- );
6729
+ ) });
6710
6730
  }
6711
6731
  function monthDiff(from, to) {
6712
6732
  return (to.getFullYear() - from.getFullYear()) * 12 + (to.getMonth() - from.getMonth());
@@ -6734,6 +6754,8 @@ function DatePickerCalendar({ className }) {
6734
6754
  const today = (0, import_react39.useMemo)(() => startOfDay(/* @__PURE__ */ new Date()), []);
6735
6755
  const days = (0, import_react39.useMemo)(() => getCalendarDays(viewMonth), [viewMonth]);
6736
6756
  const prevViewMonthRef = (0, import_react39.useRef)(viewMonth);
6757
+ const dayGridRef = (0, import_react39.useRef)(null);
6758
+ const pendingDayFocusRef = (0, import_react39.useRef)(false);
6737
6759
  const [monthSlide, setMonthSlide] = (0, import_react39.useState)(null);
6738
6760
  const isMonthPanel = activePanel === "month";
6739
6761
  const isTimePanel = activePanel === "time" && enableTime;
@@ -6790,40 +6812,40 @@ function DatePickerCalendar({ className }) {
6790
6812
  outgoingMonth: previous
6791
6813
  });
6792
6814
  }, [contentView, panel, viewMonth]);
6815
+ (0, import_react39.useLayoutEffect)(() => {
6816
+ if (!pendingDayFocusRef.current || !focusedDay) return;
6817
+ const grid = dayGridRef.current;
6818
+ if (!grid) return;
6819
+ const target = grid.querySelector(
6820
+ `button[data-day="${formatIsoDate(focusedDay)}"]`
6821
+ );
6822
+ if (!target || target.disabled) return;
6823
+ pendingDayFocusRef.current = false;
6824
+ target.focus();
6825
+ }, [focusedDay, days, monthSlide]);
6793
6826
  const outgoingDays = (0, import_react39.useMemo)(
6794
6827
  () => monthSlide ? getCalendarDays(monthSlide.outgoingMonth) : null,
6795
6828
  [monthSlide]
6796
6829
  );
6797
6830
  const handleDayKeyDown = (event, date) => {
6798
- let next = null;
6831
+ let deltaDays = 0;
6799
6832
  switch (event.key) {
6800
6833
  case "ArrowLeft":
6801
- next = new Date(date);
6802
- next.setDate(next.getDate() - 1);
6834
+ deltaDays = -1;
6803
6835
  break;
6804
6836
  case "ArrowRight":
6805
- next = new Date(date);
6806
- next.setDate(next.getDate() + 1);
6837
+ deltaDays = 1;
6807
6838
  break;
6808
6839
  case "ArrowUp":
6809
- next = new Date(date);
6810
- next.setDate(next.getDate() - 7);
6840
+ deltaDays = -7;
6811
6841
  break;
6812
6842
  case "ArrowDown":
6813
- next = new Date(date);
6814
- next.setDate(next.getDate() + 7);
6843
+ deltaDays = 7;
6815
6844
  break;
6816
6845
  case "Home":
6817
- next = new Date(date.getFullYear(), date.getMonth(), 1);
6818
- break;
6819
6846
  case "End":
6820
- next = new Date(date.getFullYear(), date.getMonth() + 1, 0);
6821
- break;
6822
6847
  case "PageUp":
6823
- next = addMonths(date, event.shiftKey ? -12 : -1);
6824
- break;
6825
6848
  case "PageDown":
6826
- next = addMonths(date, event.shiftKey ? 12 : 1);
6827
6849
  break;
6828
6850
  case "Enter":
6829
6851
  case " ":
@@ -6834,8 +6856,31 @@ function DatePickerCalendar({ className }) {
6834
6856
  return;
6835
6857
  }
6836
6858
  event.preventDefault();
6837
- if (!next) return;
6859
+ let next;
6860
+ if (event.key === "Home") {
6861
+ next = new Date(date.getFullYear(), date.getMonth(), 1);
6862
+ } else if (event.key === "End") {
6863
+ next = new Date(date.getFullYear(), date.getMonth() + 1, 0);
6864
+ } else if (event.key === "PageUp") {
6865
+ next = addMonths(date, event.shiftKey ? -12 : -1);
6866
+ } else if (event.key === "PageDown") {
6867
+ next = addMonths(date, event.shiftKey ? 12 : 1);
6868
+ } else {
6869
+ next = new Date(date);
6870
+ next.setDate(next.getDate() + deltaDays);
6871
+ let guard = 0;
6872
+ while (isDateDisabled(next, minDate, maxDate) && guard < 366) {
6873
+ next.setDate(next.getDate() + (deltaDays > 0 ? 1 : -1));
6874
+ guard += 1;
6875
+ }
6876
+ }
6838
6877
  const normalized = startOfDay(next);
6878
+ if (isDateDisabled(normalized, minDate, maxDate)) return;
6879
+ pendingDayFocusRef.current = true;
6880
+ if (mode === "single") {
6881
+ selectDate(normalized, { close: false, switchToTime: false });
6882
+ return;
6883
+ }
6839
6884
  setFocusedDay(normalized);
6840
6885
  if (!isSameMonth(normalized, viewMonth)) {
6841
6886
  setViewMonth(new Date(normalized.getFullYear(), normalized.getMonth(), 1));
@@ -6889,6 +6934,7 @@ function DatePickerCalendar({ className }) {
6889
6934
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6890
6935
  "div",
6891
6936
  {
6937
+ ref: dayGridRef,
6892
6938
  className: "aviala-datepicker-calendar__grid",
6893
6939
  role: "grid",
6894
6940
  "aria-labelledby": `${calendarId}-label`,
@@ -6908,11 +6954,13 @@ function DatePickerCalendar({ className }) {
6908
6954
  const isToday = isSameDay(day, today);
6909
6955
  const dayDisabled = isDateDisabled(day, minDate, maxDate);
6910
6956
  const focused = focusedDay ? isSameDay(day, focusedDay) : false;
6957
+ const dayKey = formatIsoDate(day);
6911
6958
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
6912
6959
  "button",
6913
6960
  {
6914
6961
  type: "button",
6915
6962
  role: "gridcell",
6963
+ "data-day": dayKey,
6916
6964
  tabIndex: focused || !focusedDay && isToday ? 0 : -1,
6917
6965
  className: "aviala-datepicker-day aviala-focus-ring",
6918
6966
  "data-outside": outside ? "true" : void 0,
@@ -6942,7 +6990,7 @@ function DatePickerCalendar({ className }) {
6942
6990
  isToday ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "aviala-datepicker-day__today-dot", "aria-hidden": true }) : null
6943
6991
  ]
6944
6992
  },
6945
- day.toISOString()
6993
+ dayKey
6946
6994
  );
6947
6995
  })
6948
6996
  },
@@ -7606,21 +7654,27 @@ var ModalContent = (0, import_react43.forwardRef)(
7606
7654
  onOpenAutoFocus,
7607
7655
  ...props
7608
7656
  }, ref) => {
7609
- const content = /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
7610
- showOverlay ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ModalOverlay, {}) : null,
7611
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
7612
- DialogPrimitive.Content,
7613
- {
7614
- ref,
7615
- className: cn(modalContentVariants({ size }), className),
7616
- onOpenAutoFocus,
7617
- ...props,
7618
- children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "aviala-modal-content__surface", children })
7619
- }
7620
- )
7657
+ const overlay = showOverlay ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ModalOverlay, {}) : null;
7658
+ const panel = /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
7659
+ DialogPrimitive.Content,
7660
+ {
7661
+ ref,
7662
+ className: cn(modalContentVariants({ size }), className),
7663
+ onOpenAutoFocus,
7664
+ ...props,
7665
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "aviala-modal-content__surface", children })
7666
+ }
7667
+ );
7668
+ if (!portalled) {
7669
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
7670
+ overlay,
7671
+ panel
7672
+ ] });
7673
+ }
7674
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(DialogPrimitive.Portal, { children: [
7675
+ overlay,
7676
+ panel
7621
7677
  ] });
7622
- if (!portalled) return content;
7623
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(DialogPrimitive.Portal, { children: content });
7624
7678
  }
7625
7679
  );
7626
7680
  ModalContent.displayName = DialogPrimitive.Content.displayName;
@@ -7637,9 +7691,9 @@ var ModalHeader = (0, import_react43.forwardRef)(
7637
7691
  showIcon ? renderModalIcon(icon) : null,
7638
7692
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "aviala-modal__header-main", children }),
7639
7693
  showClose ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(DialogPrimitive.Close, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
7640
- Link,
7694
+ Button,
7641
7695
  {
7642
- level: "text",
7696
+ type: "button",
7643
7697
  mode: "noBackgroundCustom",
7644
7698
  iconOnly: true,
7645
7699
  "aria-label": closeLabel,