@olundot/ui 0.23.1 → 0.25.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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @olundot/ui
2
2
 
3
+ ## 0.25.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a848a56: DataTable `isRowSelectable`가 `'hidden'`을 돌려주면 그 행의 선택 칸을 비웁니다.
8
+
9
+ - 폴더처럼 처음부터 고를 대상이 아닌 행에 씁니다. 체크박스 없이 같은 폭의 빈 칸을 둬 열이 어긋나지 않습니다. 1024px 미만 카드도 제목이 같은 자리에서 시작합니다.
10
+ - 선택 규칙은 `false`(막힌 행)와 같습니다. 전체 선택, 선택 개수, `onSelectionChange`에서 빠지고 `selectedIds`에 들어 있어도 선택으로 그리지 않습니다.
11
+ - 반환 타입만 `boolean | 'hidden'`으로 넓혔습니다. `boolean`을 돌려주는 코드는 그대로 동작합니다.
12
+
13
+ ## 0.24.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 052cb2d: DatePicker 기간 고르기를 다듬고 기간 이동을 더했습니다.
18
+
19
+ - 1024px 이상 `day` 기간 팝오버는 두 달을 나란히 보입니다(폭 558px). 이전 달, 다음 달 버튼은 양 끝에 하나씩이고 한 달씩 넘깁니다. 키보드는 두 달 사이를 그대로 넘습니다.
20
+ - `day` 기간에도 끝 버튼 「시작일」「종료일」을 둡니다. 누른 끝만 고칩니다. 두 끝이 찬 뒤 시작을 고르면 종료는 그대로 두고 반영합니다. 1024px 미만 시트에도 보입니다.
21
+ - 시작을 고른 뒤 가리킨 날(마우스, 키보드 포커스)까지 범위를 옅게 미리 보입니다.
22
+ - 새 prop `lockStart`(시작 끝 잠금)와 `endMin`(종료 전용 하한)을 더했습니다.
23
+ - 새 prop `onStep`, `prevStepDisabled`, `nextStepDisabled`: 칸 양옆에 「이전 기간」「다음 기간」 버튼을 두고, 옮길 폭은 사용하는 쪽이 정합니다.
24
+
3
25
  ## 0.23.1
4
26
 
5
27
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -396,6 +396,15 @@ interface DatePickerBaseProps {
396
396
  className?: string;
397
397
  "aria-label"?: string;
398
398
  "aria-describedby"?: string;
399
+ /**
400
+ * Shows "이전 기간" and "다음 기간" buttons beside the field, for views and filters that page
401
+ * through periods. Called with -1 or 1; the caller moves the value by its own step.
402
+ */
403
+ onStep?: (direction: -1 | 1) => void;
404
+ /** Disables "이전 기간". */
405
+ prevStepDisabled?: boolean;
406
+ /** Disables "다음 기간" (the next period would pass `max`, for example). */
407
+ nextStepDisabled?: boolean;
399
408
  }
400
409
  interface DatePickerSingleProps extends DatePickerBaseProps {
401
410
  selectionMode?: "single";
@@ -403,6 +412,8 @@ interface DatePickerSingleProps extends DatePickerBaseProps {
403
412
  value: string;
404
413
  onChange: (value: string) => void;
405
414
  maxDays?: never;
415
+ lockStart?: never;
416
+ endMin?: never;
406
417
  }
407
418
  interface DatePickerRangeProps extends DatePickerBaseProps {
408
419
  selectionMode: "range";
@@ -412,6 +423,10 @@ interface DatePickerRangeProps extends DatePickerBaseProps {
412
423
  onChange: (value: DateRangeValue) => void;
413
424
  /** Most days the range spans, the start day included. */
414
425
  maxDays?: number;
426
+ /** Keeps the start as it is: its end button is disabled and only the end is picked. Give the reason in `description`. */
427
+ lockStart?: boolean;
428
+ /** Earliest end, in the value format. Unlike `min`, it leaves the start alone. With it, an earlier end day no longer swaps the ends. */
429
+ endMin?: string;
415
430
  }
416
431
  type DatePickerProps = DatePickerSingleProps | DatePickerRangeProps;
417
432
  declare function DatePicker(props: DatePickerProps): react_jsx_runtime.JSX.Element;
@@ -1192,9 +1207,11 @@ interface DataTableBaseProps<T> extends Omit<React.HTMLAttributes<HTMLDivElement
1192
1207
  /**
1193
1208
  * With `selectable`, `false` blocks a row's selection: its checkbox is disabled, it is never
1194
1209
  * drawn selected or passed to `onSelectionChange`, and select-all counts only the other rows.
1210
+ * `'hidden'` blocks it the same way and draws no checkbox, for a row that is never selected (a
1211
+ * folder among items); its slot stays empty at the checkbox width, so the columns line up.
1195
1212
  * Navigating, expanding and the row menu still work.
1196
1213
  */
1197
- isRowSelectable?: (row: T) => boolean;
1214
+ isRowSelectable?: (row: T) => boolean | 'hidden';
1198
1215
  selectedIds?: Set<string>;
1199
1216
  onSelectionChange?: (ids: Set<string>) => void;
1200
1217
  /** Unit in the screen-reader selection message — `12명 선택됨`. Defaults to `개`. */
package/dist/index.js CHANGED
@@ -1887,7 +1887,7 @@ Select.displayName = "Select";
1887
1887
 
1888
1888
  // src/components/ui/date-picker.tsx
1889
1889
  import * as React19 from "react";
1890
- import { AlertCircle as AlertCircle4, Calendar as CalendarIcon, ChevronLeft, ChevronRight } from "lucide-react";
1890
+ import { AlertCircle as AlertCircle4, Calendar as CalendarIcon, ChevronLeft, ChevronRight, Lock } from "lucide-react";
1891
1891
 
1892
1892
  // src/components/ui/dialog.tsx
1893
1893
  import * as React17 from "react";
@@ -3704,7 +3704,7 @@ function formatRangeParts(value, granularity, now) {
3704
3704
  import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
3705
3705
  var WEEKDAYS = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
3706
3706
  var HOURS = Array.from({ length: 24 }, (_, hour) => hour);
3707
- var POPOVER_WIDTH = { day: 280, minute: 424 };
3707
+ var POPOVER_WIDTH = { day: 280, dayRange: 558, minute: 424 };
3708
3708
  var COLLISION_PADDING = 8;
3709
3709
  var pad22 = (n) => String(n).padStart(2, "0");
3710
3710
  var isoDay = (parts) => `${parts.year}-${pad22(parts.month)}-${pad22(parts.day)}`;
@@ -3742,11 +3742,17 @@ function DatePicker(props) {
3742
3742
  id,
3743
3743
  className,
3744
3744
  "aria-label": ariaLabel,
3745
- "aria-describedby": ariaDescribedBy
3745
+ "aria-describedby": ariaDescribedBy,
3746
+ onStep,
3747
+ prevStepDisabled = false,
3748
+ nextStepDisabled = false
3746
3749
  } = props;
3747
3750
  const range = props.selectionMode === "range";
3748
3751
  const minute = granularity === "minute";
3749
3752
  const maxDays = range ? props.maxDays : void 0;
3753
+ const lockStart = range && Boolean(props.lockStart);
3754
+ const endMinValue = range ? props.endMin : void 0;
3755
+ const popoverKind = minute ? "minute" : range ? "dayRange" : "day";
3750
3756
  const field = useFormFieldControl();
3751
3757
  const generatedId = React19.useId();
3752
3758
  const baseId = `date-picker-${generatedId.replace(/:/g, "")}`;
@@ -3760,14 +3766,18 @@ function DatePicker(props) {
3760
3766
  const parts = parseValue(max, granularity);
3761
3767
  return parts && !minute ? { ...parts, hour: 23, minute: 59 } : parts;
3762
3768
  }, [max, granularity, minute]);
3769
+ const endMinParts = React19.useMemo(() => parseValue(endMinValue, granularity), [endMinValue, granularity]);
3770
+ const swapEnds = !minute && !lockStart && !endMinParts;
3763
3771
  const [open, setOpen] = React19.useState(false);
3764
3772
  const [layer, setLayer] = React19.useState("popover");
3765
3773
  const [align, setAlign] = React19.useState("start");
3766
3774
  const [draft, setDraft] = React19.useState({ from: "", to: "" });
3767
- const [activeEnd, setActiveEnd] = React19.useState("from");
3775
+ const [pickedEnd, setActiveEnd] = React19.useState("from");
3776
+ const activeEnd = lockStart ? "to" : pickedEnd;
3768
3777
  const [timeOpen, setTimeOpen] = React19.useState(false);
3769
3778
  const [view, setView] = React19.useState(() => ({ year: 2e3, month: 1 }));
3770
3779
  const [focusDay, setFocusDay] = React19.useState(() => todayParts(/* @__PURE__ */ new Date()));
3780
+ const [pointDay, setPointDay] = React19.useState(null);
3771
3781
  const triggerRef = React19.useRef(null);
3772
3782
  const [outerLabel, setOuterLabel] = React19.useState(void 0);
3773
3783
  const [windowName, setWindowName] = React19.useState(void 0);
@@ -3807,7 +3817,7 @@ function DatePicker(props) {
3807
3817
  setOuterLabel(labelTextOf(triggerRef.current));
3808
3818
  }, [ariaLabel, label, field, triggerId]);
3809
3819
  function startView() {
3810
- const anchor = range ? parseValue(rangeValue.from, granularity) ?? parseValue(rangeValue.to, granularity) : parseValue(singleValue, granularity);
3820
+ const anchor = range ? (lockStart ? parseValue(rangeValue.to, granularity) : null) ?? parseValue(rangeValue.from, granularity) ?? parseValue(rangeValue.to, granularity) : parseValue(singleValue, granularity);
3811
3821
  return anchor ?? parseValue(initialMonth, granularity) ?? todayParts(/* @__PURE__ */ new Date());
3812
3822
  }
3813
3823
  function handleOpenChange(next) {
@@ -3819,13 +3829,14 @@ function DatePicker(props) {
3819
3829
  setDraft(rangeValue);
3820
3830
  setActiveEnd(range && rangeValue.from && !rangeValue.to ? "to" : "from");
3821
3831
  setTimeOpen(false);
3832
+ setPointDay(null);
3822
3833
  followed.current = rangeValue;
3823
3834
  lastEmitted.current = null;
3824
3835
  setWindowName(currentWindowName());
3825
3836
  const wide = isWideViewport();
3826
3837
  setLayer(wide ? "popover" : "sheet");
3827
3838
  const box = triggerRef.current?.getBoundingClientRect();
3828
- const width = POPOVER_WIDTH[granularity];
3839
+ const width = POPOVER_WIDTH[popoverKind];
3829
3840
  setAlign(box && typeof window !== "undefined" && box.left + width > window.innerWidth - COLLISION_PADDING ? "end" : "start");
3830
3841
  }
3831
3842
  setOpen(next);
@@ -3834,13 +3845,25 @@ function DatePicker(props) {
3834
3845
  const toParts = parseValue(shownRange.to, granularity);
3835
3846
  const singleParts = parseValue(singleValue, granularity);
3836
3847
  const editing = range ? activeEnd === "from" ? fromParts : toParts : singleParts;
3837
- const editMin = range && activeEnd === "to" && fromParts && minute ? minParts && minuteKey(minParts) > minuteKey(fromParts) ? minParts : fromParts : minParts;
3848
+ const editMin = range && activeEnd === "to" ? [minParts, minute ? fromParts : null, endMinParts].reduce(
3849
+ (latest, parts) => parts && (!latest || minuteKey(parts) > minuteKey(latest)) ? parts : latest,
3850
+ null
3851
+ ) : minParts;
3852
+ const editMax = (() => {
3853
+ if (!range || activeEnd !== "to" || !fromParts || maxDays === void 0) return maxParts;
3854
+ const last = { ...addDays(fromParts, maxDays - 1), hour: 23, minute: 59 };
3855
+ return maxParts && minuteKey(maxParts) < minuteKey(last) ? maxParts : last;
3856
+ })();
3838
3857
  function commitRange(next, edited) {
3839
3858
  const from = parseValue(next.from, granularity);
3840
3859
  const to = parseValue(next.to, granularity);
3860
+ if (edited === "to" && to && (editMin && minuteKey(to) < minuteKey(editMin) || editMax && minuteKey(to) > minuteKey(editMax))) {
3861
+ return draft;
3862
+ }
3841
3863
  let value = next;
3842
3864
  if (from && to && (minuteKey(from) > minuteKey(to) || maxDays !== void 0 && daysBetween(from, to) > maxDays - 1)) {
3843
3865
  const cleared = edited === "from" ? "to" : "from";
3866
+ if (lockStart && cleared === "from") return draft;
3844
3867
  value = cleared === "to" ? { from: next.from, to: "" } : { from: "", to: next.to };
3845
3868
  setActiveEnd(cleared);
3846
3869
  }
@@ -3859,16 +3882,11 @@ function DatePicker(props) {
3859
3882
  function dayDisabled(cell) {
3860
3883
  if (minParts && dayKey(cell) < dayKey(minParts)) return true;
3861
3884
  if (maxParts && dayKey(cell) > dayKey(maxParts)) return true;
3862
- if (!range) return false;
3863
- if (minute) {
3864
- if (activeEnd === "to" && fromParts) {
3865
- if (dayKey(cell) < dayKey(fromParts)) return true;
3866
- if (maxDays !== void 0 && daysBetween(fromParts, cell) > maxDays - 1) return true;
3867
- }
3868
- return false;
3869
- }
3870
- if (fromParts && !toParts && maxDays !== void 0) return daysBetween(fromParts, cell) > maxDays - 1;
3871
- return false;
3885
+ if (!range || activeEnd !== "to") return false;
3886
+ if (endMinParts && dayKey(cell) < dayKey(endMinParts)) return true;
3887
+ if (!fromParts) return false;
3888
+ if (!swapEnds && dayKey(cell) < dayKey(fromParts)) return true;
3889
+ return maxDays !== void 0 && daysBetween(fromParts, cell) > maxDays - 1;
3872
3890
  }
3873
3891
  function selectDay(cell) {
3874
3892
  if (dayDisabled(cell)) return;
@@ -3879,13 +3897,16 @@ function DatePicker(props) {
3879
3897
  return;
3880
3898
  }
3881
3899
  if (!minute) {
3882
- if (!fromParts || toParts) {
3883
- setDraft({ from: formatValue(cell, granularity), to: "" });
3900
+ const picked = formatValue(cell, granularity);
3901
+ const filled = Boolean(fromParts && toParts);
3902
+ const next = activeEnd === "from" ? { from: picked, to: draft.to } : swapEnds && fromParts && dayKey(cell) < dayKey(fromParts) ? { from: picked, to: draft.from } : { from: draft.from, to: picked };
3903
+ const value = commitRange(next, activeEnd);
3904
+ if (!value.from || !value.to) {
3905
+ setActiveEnd(value.from ? "to" : "from");
3884
3906
  return;
3885
3907
  }
3886
- const picked = formatValue(cell, granularity);
3887
- commitRange(dayKey(cell) < dayKey(fromParts) ? { from: picked, to: draft.from } : { from: draft.from, to: picked }, "to");
3888
- setOpen(false);
3908
+ if (activeEnd === "to" || !filled) setOpen(false);
3909
+ else setActiveEnd("to");
3889
3910
  return;
3890
3911
  }
3891
3912
  if (activeEnd === "from") {
@@ -3893,7 +3914,7 @@ function DatePicker(props) {
3893
3914
  if (!commitRange({ from: formatValue(nextFrom, granularity), to: draft.to }, "from").to) setActiveEnd("to");
3894
3915
  return;
3895
3916
  }
3896
- const nextTo = clampParts({ ...baseFor("to"), year: cell.year, month: cell.month, day: cell.day }, editMin, maxParts);
3917
+ const nextTo = clampParts({ ...baseFor("to"), year: cell.year, month: cell.month, day: cell.day }, editMin, editMax);
3897
3918
  commitRange({ from: draft.from, to: formatValue(nextTo, granularity) }, "to");
3898
3919
  }
3899
3920
  function changeTime(patch) {
@@ -3901,9 +3922,18 @@ function DatePicker(props) {
3901
3922
  props.onChange(formatValue(clampParts({ ...baseFor(null), ...patch }, minParts, maxParts), granularity));
3902
3923
  return;
3903
3924
  }
3904
- const next = formatValue(clampParts({ ...baseFor(activeEnd), ...patch }, editMin, maxParts), granularity);
3925
+ const next = formatValue(clampParts({ ...baseFor(activeEnd), ...patch }, editMin, editMax), granularity);
3905
3926
  commitRange(activeEnd === "from" ? { from: next, to: draft.to } : { from: draft.from, to: next }, activeEnd);
3906
3927
  }
3928
+ function activateEnd(end, kind) {
3929
+ setActiveEnd(end);
3930
+ const parts = end === "from" ? fromParts : toParts;
3931
+ if (!parts) return;
3932
+ const shown = kind === "popover" && popoverKind === "dayRange" ? 2 : 1;
3933
+ const offset = (parts.year - view.year) * 12 + parts.month - view.month;
3934
+ if (offset < 0 || offset >= shown) setView({ year: parts.year, month: parts.month });
3935
+ setFocusDay({ ...parts, hour: 0, minute: 0 });
3936
+ }
3907
3937
  const endPlaceholder = minute ? "\uC885\uB8CC \uC77C\uC2DC" : "\uC885\uB8CC\uC77C";
3908
3938
  let summary;
3909
3939
  let summaryText = "";
@@ -3995,11 +4025,25 @@ function DatePicker(props) {
3995
4025
  }
3996
4026
  );
3997
4027
  const today = todayParts(now);
4028
+ const preview = (() => {
4029
+ if (!range || !pointDay || dayDisabled(pointDay)) return null;
4030
+ const edited = activeEnd === "from" ? fromParts : toParts;
4031
+ if (edited && sameDay(pointDay, edited)) return null;
4032
+ if (activeEnd === "to" && fromParts) {
4033
+ return dayKey(pointDay) < dayKey(fromParts) ? { from: pointDay, to: fromParts, kept: fromParts } : { from: fromParts, to: pointDay, kept: fromParts };
4034
+ }
4035
+ if (activeEnd === "from" && toParts && dayKey(pointDay) <= dayKey(toParts) && (maxDays === void 0 || daysBetween(pointDay, toParts) <= maxDays - 1)) {
4036
+ return { from: pointDay, to: toParts, kept: toParts };
4037
+ }
4038
+ return null;
4039
+ })();
3998
4040
  const selectedFor = (cell) => {
3999
4041
  if (!range) return Boolean(singleParts && sameDay(cell, singleParts));
4042
+ if (preview) return sameDay(cell, preview.kept);
4000
4043
  return Boolean(fromParts && sameDay(cell, fromParts) || toParts && sameDay(cell, toParts));
4001
4044
  };
4002
- const inRange = (cell) => Boolean(range && fromParts && toParts && dayKey(cell) > dayKey(fromParts) && dayKey(cell) < dayKey(toParts));
4045
+ const inRange = (cell) => Boolean(range && !preview && fromParts && toParts && dayKey(cell) > dayKey(fromParts) && dayKey(cell) < dayKey(toParts));
4046
+ const inPreview = (cell) => Boolean(preview && !sameDay(cell, preview.kept) && dayKey(cell) >= dayKey(preview.from) && dayKey(cell) <= dayKey(preview.to));
4003
4047
  const focusCalendar = React19.useRef(false);
4004
4048
  React19.useEffect(() => {
4005
4049
  const active = typeof document !== "undefined" ? document.activeElement : null;
@@ -4032,6 +4076,7 @@ function DatePicker(props) {
4032
4076
  {
4033
4077
  kind,
4034
4078
  baseId,
4079
+ months: kind === "popover" && popoverKind === "dayRange" ? 2 : 1,
4035
4080
  view,
4036
4081
  onView: setView,
4037
4082
  focusDay,
@@ -4040,6 +4085,8 @@ function DatePicker(props) {
4040
4085
  isDisabled: dayDisabled,
4041
4086
  isSelected: selectedFor,
4042
4087
  isInRange: inRange,
4088
+ isPreview: inPreview,
4089
+ onPoint: setPointDay,
4043
4090
  onSelect: selectDay
4044
4091
  }
4045
4092
  );
@@ -4049,12 +4096,14 @@ function DatePicker(props) {
4049
4096
  const timeRowValue = editing ? `${pad22(editing.hour)}:${pad22(editing.minute)}` : "-";
4050
4097
  const panel = (kind) => /* @__PURE__ */ jsxs14("div", { "data-slot": "date-picker-panel", "data-layer": kind, "data-picker": baseId, className: "flex flex-col", children: [
4051
4098
  popoverHeader != null ? /* @__PURE__ */ jsx16("div", { className: "mb-[var(--space-3)]", children: popoverHeader }) : null,
4052
- range && minute ? /* @__PURE__ */ jsx16(
4099
+ range ? /* @__PURE__ */ jsx16(
4053
4100
  EndTabs,
4054
4101
  {
4055
4102
  kind,
4103
+ names: minute ? ["\uC2DC\uC791 \uC77C\uC2DC", "\uC885\uB8CC \uC77C\uC2DC"] : ["\uC2DC\uC791\uC77C", "\uC885\uB8CC\uC77C"],
4104
+ locked: lockStart ? "from" : void 0,
4056
4105
  active: activeEnd,
4057
- onActivate: setActiveEnd,
4106
+ onActivate: (end) => activateEnd(end, kind),
4058
4107
  from: fromParts ? formatSingle(shownRange.from, granularity, now) : "",
4059
4108
  to: toParts ? formatSingle(shownRange.to, granularity, now) : ""
4060
4109
  }
@@ -4093,7 +4142,7 @@ function DatePicker(props) {
4093
4142
  ) : null
4094
4143
  ] })
4095
4144
  ] });
4096
- const control = /* @__PURE__ */ jsxs14(Fragment3, { children: [
4145
+ const picker = /* @__PURE__ */ jsxs14(Fragment3, { children: [
4097
4146
  /* @__PURE__ */ jsxs14(Popover, { open: open && layer === "popover", onOpenChange: handleOpenChange, children: [
4098
4147
  /* @__PURE__ */ jsx16(PopoverAnchor, { asChild: true, children: trigger }),
4099
4148
  /* @__PURE__ */ jsx16(
@@ -4102,7 +4151,7 @@ function DatePicker(props) {
4102
4151
  align,
4103
4152
  sideOffset: 4,
4104
4153
  collisionPadding: COLLISION_PADDING,
4105
- className: cn("p-[var(--space-3)]", minute ? "w-[424px]" : "w-[280px]"),
4154
+ className: cn("p-[var(--space-3)]", { day: "w-[280px]", dayRange: "w-[558px]", minute: "w-[424px]" }[popoverKind]),
4106
4155
  "aria-label": titleText,
4107
4156
  onOpenAutoFocus: (event) => {
4108
4157
  event.preventDefault();
@@ -4125,6 +4174,34 @@ function DatePicker(props) {
4125
4174
  ] }),
4126
4175
  /* @__PURE__ */ jsx16(Dialog, { open: open && layer === "sheet", onOpenChange: handleOpenChange, title: titleText, size: "sm", children: /* @__PURE__ */ jsx16("div", { className: "mx-auto w-full max-w-[360px]", children: panel("sheet") }) })
4127
4176
  ] });
4177
+ const stepSize = { sm: "sm", md: "md", lg: "lg" };
4178
+ const control = onStep ? /* @__PURE__ */ jsxs14("div", { "data-slot": "date-picker-stepper", className: cn("flex items-center gap-[var(--space-1)]", fullWidth ? "w-full" : "w-fit"), children: [
4179
+ /* @__PURE__ */ jsx16(
4180
+ IconButton,
4181
+ {
4182
+ icon: ChevronLeft,
4183
+ appearance: "outline",
4184
+ tone: "neutral",
4185
+ size: stepSize[size],
4186
+ "aria-label": "\uC774\uC804 \uAE30\uAC04",
4187
+ disabled: disabled || prevStepDisabled,
4188
+ onClick: () => onStep(-1)
4189
+ }
4190
+ ),
4191
+ /* @__PURE__ */ jsx16("div", { className: cn("min-w-0", fullWidth && "flex-1"), children: picker }),
4192
+ /* @__PURE__ */ jsx16(
4193
+ IconButton,
4194
+ {
4195
+ icon: ChevronRight,
4196
+ appearance: "outline",
4197
+ tone: "neutral",
4198
+ size: stepSize[size],
4199
+ "aria-label": "\uB2E4\uC74C \uAE30\uAC04",
4200
+ disabled: disabled || nextStepDisabled,
4201
+ onClick: () => onStep(1)
4202
+ }
4203
+ )
4204
+ ] }) : picker;
4128
4205
  if (!topLabel && !showDescription && !showMessage) return control;
4129
4206
  return /* @__PURE__ */ jsxs14("div", { "data-slot": "date-picker-field", className: cn("flex flex-col gap-[var(--space-1)]", fullWidth ? "w-full" : "w-fit"), children: [
4130
4207
  topLabel ? /* @__PURE__ */ jsx16(
@@ -4148,51 +4225,69 @@ function DatePicker(props) {
4148
4225
  DatePicker.displayName = "DatePicker";
4149
4226
  function EndTabs({
4150
4227
  kind,
4228
+ names,
4229
+ locked,
4151
4230
  active,
4152
4231
  onActivate,
4153
4232
  from,
4154
4233
  to
4155
4234
  }) {
4156
4235
  const ends = [
4157
- { end: "from", name: "\uC2DC\uC791 \uC77C\uC2DC", value: from },
4158
- { end: "to", name: "\uC885\uB8CC \uC77C\uC2DC", value: to }
4236
+ { end: "from", name: names[0], value: from },
4237
+ { end: "to", name: names[1], value: to }
4159
4238
  ];
4160
- return /* @__PURE__ */ jsx16("div", { "data-slot": "date-picker-ends", className: cn("mb-[var(--space-3)] grid grid-cols-2 gap-[var(--space-2)]", kind === "sheet" && "mb-[var(--space-4)]"), children: ends.map(({ end, name, value }) => /* @__PURE__ */ jsxs14(
4161
- "button",
4162
- {
4163
- type: "button",
4164
- "data-slot": "date-picker-end",
4165
- "aria-pressed": active === end,
4166
- onClick: () => onActivate(end),
4167
- className: cn(
4168
- "flex min-w-0 flex-col items-start rounded-[var(--radius-md)] border px-[var(--space-3)] py-[var(--space-2)] text-left",
4169
- "outline-none focus-visible:shadow-[0_0_0_2px_var(--focus-ring)]",
4170
- active === end ? "border-[var(--border-strong)] bg-[var(--action-selected)]" : "border-[var(--border)] hover:bg-[var(--action-hover)]"
4171
- ),
4172
- children: [
4173
- /* @__PURE__ */ jsx16("span", { className: "text-[11px] leading-snug text-text-tertiary", children: name }),
4174
- /* @__PURE__ */ jsx16("span", { className: cn("w-full truncate text-[13px] font-semibold leading-snug font-numeric tabular-nums", value ? "text-foreground" : "text-text-tertiary"), children: value || "-" })
4175
- ]
4176
- },
4177
- end
4178
- )) });
4239
+ return /* @__PURE__ */ jsx16("div", { "data-slot": "date-picker-ends", className: cn("mb-[var(--space-3)] grid grid-cols-2 gap-[var(--space-2)]", kind === "sheet" && "mb-[var(--space-4)]"), children: ends.map(({ end, name, value }) => {
4240
+ const isLocked = locked === end;
4241
+ return /* @__PURE__ */ jsxs14(
4242
+ "button",
4243
+ {
4244
+ type: "button",
4245
+ "data-slot": "date-picker-end",
4246
+ "aria-pressed": active === end,
4247
+ disabled: isLocked,
4248
+ onClick: () => onActivate(end),
4249
+ className: cn(
4250
+ "flex min-w-0 flex-col items-start rounded-[var(--radius-md)] border px-[var(--space-3)] py-[var(--space-2)] text-left",
4251
+ "outline-none focus-visible:shadow-[0_0_0_2px_var(--focus-ring)]",
4252
+ isLocked ? "cursor-not-allowed border-[var(--border)] bg-[var(--action-disabled-bg)]" : active === end ? "border-[var(--border-strong)] bg-[var(--action-selected)]" : "border-[var(--border)] hover:bg-[var(--action-hover)]"
4253
+ ),
4254
+ children: [
4255
+ /* @__PURE__ */ jsxs14("span", { className: "flex w-full items-center justify-between gap-[var(--space-1)] text-[11px] leading-snug text-text-tertiary", children: [
4256
+ /* @__PURE__ */ jsx16("span", { children: name }),
4257
+ isLocked ? /* @__PURE__ */ jsx16(Lock, { "aria-hidden": "true", size: 12, strokeWidth: 2, className: "shrink-0" }) : null
4258
+ ] }),
4259
+ /* @__PURE__ */ jsx16(
4260
+ "span",
4261
+ {
4262
+ className: cn(
4263
+ "w-full truncate text-[13px] font-semibold leading-snug font-numeric tabular-nums",
4264
+ isLocked ? "text-text-disabled" : value ? "text-foreground" : "text-text-tertiary"
4265
+ ),
4266
+ children: value || "-"
4267
+ }
4268
+ )
4269
+ ]
4270
+ },
4271
+ end
4272
+ );
4273
+ }) });
4179
4274
  }
4180
- function Calendar({ kind, baseId, view, onView, focusDay, onFocusDay, today, isDisabled, isSelected, isInRange, onSelect }) {
4181
- const gridRef = React19.useRef(null);
4275
+ function Calendar({ kind, baseId, months, view, onView, focusDay, onFocusDay, today, isDisabled, isSelected, isInRange, isPreview, onPoint, onSelect }) {
4276
+ const rootRef = React19.useRef(null);
4182
4277
  const focusPending = React19.useRef(false);
4183
- const headingId = `${baseId}-month`;
4184
- const cells = buildCalendarCells(view.year, view.month).map((cell) => ({ ...cell, hour: 0, minute: 0 }));
4185
- const weeks = Array.from({ length: 6 }, (_, week) => cells.slice(week * 7, week * 7 + 7));
4186
- const tabbable = focusDay.year === view.year && focusDay.month === view.month ? focusDay : { ...focusDay, year: view.year, month: view.month, day: 1 };
4278
+ const shown = Array.from({ length: months }, (_, index) => addMonths(view.year, view.month, index));
4279
+ const inView = (day) => shown.some((month) => month.year === day.year && month.month === day.month);
4280
+ const tabbable = inView(focusDay) ? focusDay : { ...focusDay, year: view.year, month: view.month, day: 1 };
4187
4281
  React19.useEffect(() => {
4188
4282
  if (!focusPending.current) return;
4189
4283
  focusPending.current = false;
4190
- gridRef.current?.querySelector(`[data-date="${isoDay(focusDay)}"]`)?.focus();
4284
+ rootRef.current?.querySelector(`[data-date="${isoDay(focusDay)}"]`)?.focus();
4191
4285
  });
4192
4286
  function move(next) {
4193
4287
  focusPending.current = true;
4194
4288
  onFocusDay(next);
4195
- if (next.year !== view.year || next.month !== view.month) onView({ year: next.year, month: next.month });
4289
+ if (inView(next)) return;
4290
+ onView(dayKey(next) < dayKey({ ...view, day: 1 }) ? { year: next.year, month: next.month } : addMonths(next.year, next.month, 1 - months));
4196
4291
  }
4197
4292
  function moveMonth(delta) {
4198
4293
  onView(addMonths(view.year, view.month, delta));
@@ -4216,61 +4311,100 @@ function Calendar({ kind, baseId, view, onView, focusDay, onFocusDay, today, isD
4216
4311
  move(target());
4217
4312
  }
4218
4313
  const sheet = kind === "sheet";
4219
- return /* @__PURE__ */ jsxs14("div", { "data-slot": "date-picker-calendar", className: cn("flex flex-col", sheet ? "w-full" : "w-[256px] shrink-0"), children: [
4220
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between", children: [
4221
- /* @__PURE__ */ jsx16(IconButton, { icon: ChevronLeft, appearance: "ghost", tone: "neutral", size: sheet ? "md" : "xs", "aria-label": "\uC774\uC804 \uB2EC", onClick: () => moveMonth(-1) }),
4222
- /* @__PURE__ */ jsxs14("span", { id: headingId, "aria-live": "polite", className: "text-[13px] font-semibold text-text-primary font-numeric tabular-nums", children: [
4223
- view.year,
4224
- "\uB144 ",
4225
- view.month,
4226
- "\uC6D4"
4227
- ] }),
4228
- /* @__PURE__ */ jsx16(IconButton, { icon: ChevronRight, appearance: "ghost", tone: "neutral", size: sheet ? "md" : "xs", "aria-label": "\uB2E4\uC74C \uB2EC", onClick: () => moveMonth(1) })
4229
- ] }),
4230
- /* @__PURE__ */ jsxs14("div", { ref: gridRef, role: "grid", "aria-labelledby": headingId, "data-slot": "date-picker-grid", className: "mt-[var(--space-1)] flex flex-col gap-[2px]", children: [
4231
- /* @__PURE__ */ jsx16("div", { role: "row", className: "grid grid-cols-7 gap-[2px]", children: WEEKDAYS.map((weekday) => /* @__PURE__ */ jsx16("span", { role: "columnheader", className: "py-[var(--space-1)] text-center text-[10px] font-semibold text-text-tertiary", children: weekday }, weekday)) }),
4232
- weeks.map((week, index) => /* @__PURE__ */ jsx16("div", { role: "row", className: "grid grid-cols-7 gap-[2px]", children: week.map((cell) => {
4233
- const outside = !cell.inMonth;
4234
- const disabled = isDisabled(cell);
4235
- const selected = !outside && isSelected(cell);
4236
- const between = !outside && !selected && isInRange(cell);
4237
- const isToday = !outside && sameDay(cell, today);
4238
- return /* @__PURE__ */ jsx16(
4239
- "button",
4240
- {
4241
- type: "button",
4242
- role: "gridcell",
4243
- "data-date": outside ? void 0 : isoDay(cell),
4244
- "data-selected": selected ? "" : void 0,
4245
- "data-in-range": between ? "" : void 0,
4246
- disabled: outside,
4247
- tabIndex: !outside && sameDay(cell, tabbable) ? 0 : -1,
4248
- "aria-label": `${cell.year}\uB144 ${cell.month}\uC6D4 ${cell.day}\uC77C`,
4249
- "aria-selected": outside ? void 0 : selected,
4250
- "aria-disabled": !outside && disabled ? true : void 0,
4251
- "aria-current": isToday ? "date" : void 0,
4252
- onClick: () => {
4253
- if (outside || disabled) return;
4254
- focusPending.current = false;
4255
- onFocusDay(cell);
4256
- onSelect(cell);
4314
+ const navSize = sheet ? "md" : "xs";
4315
+ const headingId = (index) => index === 0 ? `${baseId}-month` : `${baseId}-month-${index + 1}`;
4316
+ const width = sheet ? "w-full" : "w-[256px]";
4317
+ const blankOutside = months > 1;
4318
+ return /* @__PURE__ */ jsxs14("div", { ref: rootRef, "data-slot": "date-picker-calendar", className: cn("flex flex-col", sheet ? "w-full" : "shrink-0"), children: [
4319
+ /* @__PURE__ */ jsx16("div", { className: "flex gap-[var(--space-5)]", children: shown.map((month, index) => (
4320
+ // Keyed by position, not month: the ‹ › pressed stays the same node and keeps focus.
4321
+ /* @__PURE__ */ jsxs14("div", { className: cn("flex items-center justify-between", width), children: [
4322
+ index === 0 ? /* @__PURE__ */ jsx16(IconButton, { icon: ChevronLeft, appearance: "ghost", tone: "neutral", size: navSize, "aria-label": "\uC774\uC804 \uB2EC", onClick: () => moveMonth(-1) }) : /* @__PURE__ */ jsx16("span", { "aria-hidden": "true", className: "size-[28px]" }),
4323
+ /* @__PURE__ */ jsxs14("span", { id: headingId(index), "aria-live": "polite", className: "text-[13px] font-semibold text-text-primary font-numeric tabular-nums", children: [
4324
+ month.year,
4325
+ "\uB144 ",
4326
+ month.month,
4327
+ "\uC6D4"
4328
+ ] }),
4329
+ index === months - 1 ? /* @__PURE__ */ jsx16(IconButton, { icon: ChevronRight, appearance: "ghost", tone: "neutral", size: navSize, "aria-label": "\uB2E4\uC74C \uB2EC", onClick: () => moveMonth(1) }) : /* @__PURE__ */ jsx16("span", { "aria-hidden": "true", className: "size-[28px]" })
4330
+ ] }, index)
4331
+ )) }),
4332
+ /* @__PURE__ */ jsx16(
4333
+ "div",
4334
+ {
4335
+ className: "mt-[var(--space-1)] flex gap-[var(--space-5)]",
4336
+ onMouseLeave: () => onPoint(null),
4337
+ onBlur: (event) => {
4338
+ if (!(event.relatedTarget instanceof Node && event.currentTarget.contains(event.relatedTarget))) onPoint(null);
4339
+ },
4340
+ children: shown.map((month, index) => {
4341
+ const cells = buildCalendarCells(month.year, month.month).map((cell) => ({ ...cell, hour: 0, minute: 0 }));
4342
+ const weeks = Array.from({ length: 6 }, (_, week) => cells.slice(week * 7, week * 7 + 7));
4343
+ return /* @__PURE__ */ jsxs14(
4344
+ "div",
4345
+ {
4346
+ role: "grid",
4347
+ "aria-labelledby": headingId(index),
4348
+ "data-slot": "date-picker-grid",
4349
+ className: cn("flex flex-col gap-[2px]", width),
4350
+ children: [
4351
+ /* @__PURE__ */ jsx16("div", { role: "row", className: "grid grid-cols-7 gap-[2px]", children: WEEKDAYS.map((weekday) => /* @__PURE__ */ jsx16("span", { role: "columnheader", className: "py-[var(--space-1)] text-center text-[10px] font-semibold text-text-tertiary", children: weekday }, weekday)) }),
4352
+ weeks.map((week, row) => /* @__PURE__ */ jsx16("div", { role: "row", className: "grid grid-cols-7 gap-[2px]", children: week.map((cell) => {
4353
+ const outside = !cell.inMonth;
4354
+ if (outside && blankOutside) {
4355
+ return /* @__PURE__ */ jsx16("span", { role: "gridcell", "data-outside": "", className: "aspect-square" }, isoDay(cell));
4356
+ }
4357
+ const disabled = isDisabled(cell);
4358
+ const selected = !outside && isSelected(cell);
4359
+ const between = !outside && !selected && isInRange(cell);
4360
+ const previewed = !outside && !selected && isPreview(cell);
4361
+ const isToday = !outside && sameDay(cell, today);
4362
+ return /* @__PURE__ */ jsx16(
4363
+ "button",
4364
+ {
4365
+ type: "button",
4366
+ role: "gridcell",
4367
+ "data-date": outside ? void 0 : isoDay(cell),
4368
+ "data-selected": selected ? "" : void 0,
4369
+ "data-in-range": between ? "" : void 0,
4370
+ "data-preview": previewed ? "" : void 0,
4371
+ disabled: outside,
4372
+ tabIndex: !outside && sameDay(cell, tabbable) ? 0 : -1,
4373
+ "aria-label": `${cell.year}\uB144 ${cell.month}\uC6D4 ${cell.day}\uC77C`,
4374
+ "aria-selected": outside ? void 0 : selected,
4375
+ "aria-disabled": !outside && disabled ? true : void 0,
4376
+ "aria-current": isToday ? "date" : void 0,
4377
+ onClick: () => {
4378
+ if (outside || disabled) return;
4379
+ focusPending.current = false;
4380
+ onFocusDay(cell);
4381
+ onSelect(cell);
4382
+ },
4383
+ onKeyDown: (event) => onKeyDown(event, cell),
4384
+ onMouseEnter: () => onPoint(cell),
4385
+ onFocus: () => onPoint(cell),
4386
+ className: cn(
4387
+ "flex items-center justify-center rounded-[var(--radius-sm)] text-[12px] font-numeric tabular-nums outline-none",
4388
+ sheet ? "h-[40px] text-[14px]" : "aspect-square",
4389
+ "focus-visible:shadow-[0_0_0_2px_var(--focus-ring)]",
4390
+ outside || disabled ? "cursor-default text-text-disabled" : "cursor-pointer text-text-secondary hover:bg-[var(--action-hover)]",
4391
+ between && "bg-[var(--action-selected)] text-foreground",
4392
+ previewed && "bg-[var(--action-hover)] text-foreground shadow-[inset_0_1px_0_0_var(--border-strong),inset_0_-1px_0_0_var(--border-strong)]",
4393
+ isToday && !selected && "border border-[var(--border-strong)]",
4394
+ selected && "bg-[var(--primary)] font-bold text-[var(--primary-fg)] hover:bg-[var(--primary)]"
4395
+ ),
4396
+ children: cell.day
4397
+ },
4398
+ isoDay(cell)
4399
+ );
4400
+ }) }, row))
4401
+ ]
4257
4402
  },
4258
- onKeyDown: (event) => onKeyDown(event, cell),
4259
- className: cn(
4260
- "flex items-center justify-center rounded-[var(--radius-sm)] text-[12px] font-numeric tabular-nums outline-none",
4261
- sheet ? "h-[40px] text-[14px]" : "aspect-square",
4262
- "focus-visible:shadow-[0_0_0_2px_var(--focus-ring)]",
4263
- outside || disabled ? "cursor-default text-text-disabled" : "cursor-pointer text-text-secondary hover:bg-[var(--action-hover)]",
4264
- between && "bg-[var(--action-selected)] text-foreground",
4265
- isToday && !selected && "border border-[var(--border-strong)]",
4266
- selected && "bg-[var(--primary)] font-bold text-[var(--primary-fg)] hover:bg-[var(--primary)]"
4267
- ),
4268
- children: cell.day
4269
- },
4270
- isoDay(cell)
4271
- );
4272
- }) }, index))
4273
- ] })
4403
+ `${month.year}-${month.month}`
4404
+ );
4405
+ })
4406
+ }
4407
+ )
4274
4408
  ] });
4275
4409
  }
4276
4410
  function TimeColumns({ kind, baseId, editing, hasValue, centerKey, minuteStep, min, max, onChange, onEnter, autoFocus = false }) {
@@ -4328,15 +4462,10 @@ function TimeColumn({ kind, id, name, options, selected, centerKey, onSelect, on
4328
4462
  if (!list || !item) return;
4329
4463
  list.scrollTop = item.offsetTop - list.offsetTop - (list.clientHeight - item.offsetHeight) / 2;
4330
4464
  }, [centerKey]);
4331
- const pendingFocus = React19.useRef(null);
4465
+ const [focusAsk, setFocusAsk] = React19.useState(0);
4332
4466
  React19.useEffect(() => {
4333
- if (pendingFocus.current === null) return;
4334
- listRef.current?.querySelector(`[data-value="${pendingFocus.current}"]`)?.focus();
4335
- pendingFocus.current = null;
4336
- });
4337
- function focusValue(value) {
4338
- pendingFocus.current = value;
4339
- }
4467
+ if (focusAsk) listRef.current?.querySelector('[aria-selected="true"]')?.focus();
4468
+ }, [focusAsk]);
4340
4469
  function onKeyDown(event, value) {
4341
4470
  if (event.key === "Enter") {
4342
4471
  event.preventDefault();
@@ -4368,7 +4497,7 @@ function TimeColumn({ kind, id, name, options, selected, centerKey, onSelect, on
4368
4497
  const next = target();
4369
4498
  if (!next || next.value === value) return;
4370
4499
  onSelect(next.value);
4371
- focusValue(next.value);
4500
+ setFocusAsk((count) => count + 1);
4372
4501
  }
4373
4502
  const sheet = kind === "sheet";
4374
4503
  return /* @__PURE__ */ jsxs14("div", { className: cn("flex flex-col", sheet ? "w-[88px]" : "h-full w-[56px]"), children: [
@@ -5621,7 +5750,7 @@ import * as React30 from "react";
5621
5750
  import * as React29 from "react";
5622
5751
  import { Tabs as TabsPrimitive } from "radix-ui";
5623
5752
  import { cva as cva10 } from "class-variance-authority";
5624
- import { Lock } from "lucide-react";
5753
+ import { Lock as Lock2 } from "lucide-react";
5625
5754
  import { Fragment as Fragment10, jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
5626
5755
  var tabsRootVariants = cva10("w-full", {
5627
5756
  variants: {
@@ -5894,7 +6023,7 @@ var Tabs = React29.forwardRef(
5894
6023
  /* @__PURE__ */ jsx26("span", { className: "sr-only", children: ` ${tab.badge}` })
5895
6024
  ] }) : null,
5896
6025
  tab.disabled ? /* @__PURE__ */ jsx26(
5897
- Lock,
6026
+ Lock2,
5898
6027
  {
5899
6028
  "aria-hidden": "true",
5900
6029
  "data-slot": "tabs-disabled-icon",
@@ -8524,8 +8653,19 @@ function RowCheckbox({
8524
8653
  columns,
8525
8654
  selected,
8526
8655
  selectionBlocked,
8656
+ selectionHidden,
8527
8657
  onToggle
8528
8658
  }) {
8659
+ if (selectionHidden) {
8660
+ return /* @__PURE__ */ jsx39(
8661
+ "span",
8662
+ {
8663
+ "aria-hidden": "true",
8664
+ "data-slot": "data-table-row-checkbox-placeholder",
8665
+ className: "block h-[calc(var(--space-4)+var(--space-1))] w-[calc(var(--space-4)+var(--space-1)+var(--space-3))] shrink-0"
8666
+ }
8667
+ );
8668
+ }
8529
8669
  return /* @__PURE__ */ jsx39(
8530
8670
  Checkbox,
8531
8671
  {
@@ -8636,6 +8776,7 @@ function DataTableRowView({
8636
8776
  selectable,
8637
8777
  selected,
8638
8778
  selectionBlocked,
8779
+ selectionHidden,
8639
8780
  visuallySelected,
8640
8781
  href,
8641
8782
  activatable,
@@ -8672,7 +8813,7 @@ function DataTableRowView({
8672
8813
  } : handlers.onClick,
8673
8814
  onKeyDown: handlers.onKeyDown,
8674
8815
  children: [
8675
- selectable ? /* @__PURE__ */ jsx39("div", { role: "cell", className: "px-[var(--space-4)]", children: /* @__PURE__ */ jsx39(RowCheckbox, { row, rowId, columns, selected, selectionBlocked, onToggle }) }) : null,
8816
+ selectable ? /* @__PURE__ */ jsx39("div", { role: "cell", className: "px-[var(--space-4)]", children: /* @__PURE__ */ jsx39(RowCheckbox, { row, rowId, columns, selected, selectionBlocked, selectionHidden, onToggle }) }) : null,
8676
8817
  columns.map((column, columnIndex) => {
8677
8818
  const align = column.align ?? "left";
8678
8819
  const content = getRenderedCell(row, column);
@@ -8746,6 +8887,7 @@ function DataTableMobileCardView({
8746
8887
  selectable,
8747
8888
  selected,
8748
8889
  selectionBlocked,
8890
+ selectionHidden,
8749
8891
  visuallySelected,
8750
8892
  href,
8751
8893
  activatable,
@@ -8772,7 +8914,7 @@ function DataTableMobileCardView({
8772
8914
  const navigable = linked || activatable;
8773
8915
  const opened = useOpenedOnce(expand?.expanded ?? false);
8774
8916
  const leading = selectable || content.leading != null ? /* @__PURE__ */ jsxs35("span", { className: "flex items-center gap-[var(--space-2)]", children: [
8775
- selectable ? /* @__PURE__ */ jsx39(RowCheckbox, { row, rowId, columns, selected, selectionBlocked, onToggle }) : null,
8917
+ selectable ? /* @__PURE__ */ jsx39(RowCheckbox, { row, rowId, columns, selected, selectionBlocked, selectionHidden, onToggle }) : null,
8776
8918
  content.leading
8777
8919
  ] }) : void 0;
8778
8920
  let title = content.title;
@@ -9252,9 +9394,19 @@ function DataTableInner({
9252
9394
  mobile: () => mobileListRef.current ?? mobileRef.current
9253
9395
  }
9254
9396
  });
9255
- const blockedIds = new Set(
9256
- selectable && isRowSelectable ? data.filter((row) => !isRowSelectable(row)).map((row) => getRowId(row, keyField)) : []
9257
- );
9397
+ const blockedIds = /* @__PURE__ */ new Set();
9398
+ const hiddenIds = /* @__PURE__ */ new Set();
9399
+ if (selectable && isRowSelectable) {
9400
+ data.forEach((row) => {
9401
+ const verdict = isRowSelectable(row);
9402
+ if (verdict === "hidden") {
9403
+ hiddenIds.add(getRowId(row, keyField));
9404
+ }
9405
+ if (!verdict || verdict === "hidden") {
9406
+ blockedIds.add(getRowId(row, keyField));
9407
+ }
9408
+ });
9409
+ }
9258
9410
  const selectableRowIds = rowIds.filter((id) => !blockedIds.has(id));
9259
9411
  const resolvedSelectedIds = selectedIds ?? /* @__PURE__ */ new Set();
9260
9412
  const selectedCount = selectableRowIds.filter((id) => resolvedSelectedIds.has(id)).length;
@@ -9377,6 +9529,7 @@ function DataTableInner({
9377
9529
  const getItemProps = (row, layout, index) => {
9378
9530
  const rowId = getRowId(row, keyField);
9379
9531
  const selectionBlocked = blockedIds.has(rowId);
9532
+ const selectionHidden = hiddenIds.has(rowId);
9380
9533
  const selected = !selectionBlocked && resolvedSelectedIds.has(rowId);
9381
9534
  const href = rowHref?.(row);
9382
9535
  const linked = Boolean(linkComponent && href);
@@ -9387,6 +9540,7 @@ function DataTableInner({
9387
9540
  selectable,
9388
9541
  selected,
9389
9542
  selectionBlocked,
9543
+ selectionHidden,
9390
9544
  visuallySelected: selected || rowId === activeRowId || rowId === contextMenu?.rowId,
9391
9545
  href,
9392
9546
  activatable: !linked && Boolean(href || onRowClick),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olundot/ui",
3
- "version": "0.23.1",
3
+ "version": "0.25.0",
4
4
  "description": "OLUN. shared React UI components built on OLUN design tokens",
5
5
  "license": "MIT",
6
6
  "keywords": [