@kalyx/react 1.0.0-rc.7 → 1.0.0-rc.9

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.d.cts CHANGED
@@ -495,12 +495,20 @@ interface TimePickerRootProps {
495
495
  disabled?: boolean;
496
496
  /** Read-only */
497
497
  readOnly?: boolean;
498
+ /**
499
+ * Programmatic per-slot disable predicate. Returns `true` for any `(hours, minutes)` pair
500
+ * that should be unselectable — same polarity as MUI X's `shouldDisableTime`, and the
501
+ * **inverse** of react-datepicker's `filterTime` (which returns `true` to *keep* a slot).
502
+ * Use cases: business hours, lunch breaks, blackout slots. Hours are disabled only when the
503
+ * predicate returns `true` for every step within the hour. Always receives 24-hour values.
504
+ */
505
+ filterTime?: (hours: number, minutes: number) => boolean;
498
506
  /** Override ARIA labels (defaults to English) */
499
507
  labels?: Partial<TimePickerLabels>;
500
508
  /** Child components */
501
509
  children: ReactNode;
502
510
  }
503
- declare function TimePickerRoot({ value: controlledValue, defaultValue, onChange, format, step, withSeconds, displayTimezone, disabled, readOnly, labels: labelsProp, children, }: TimePickerRootProps): react_jsx_runtime.JSX.Element;
511
+ declare function TimePickerRoot({ value: controlledValue, defaultValue, onChange, format, step, withSeconds, displayTimezone, disabled, readOnly, filterTime, labels: labelsProp, children, }: TimePickerRootProps): react_jsx_runtime.JSX.Element;
504
512
 
505
513
  interface TimePickerHourListClassNames {
506
514
  root?: string;
package/dist/index.d.ts CHANGED
@@ -495,12 +495,20 @@ interface TimePickerRootProps {
495
495
  disabled?: boolean;
496
496
  /** Read-only */
497
497
  readOnly?: boolean;
498
+ /**
499
+ * Programmatic per-slot disable predicate. Returns `true` for any `(hours, minutes)` pair
500
+ * that should be unselectable — same polarity as MUI X's `shouldDisableTime`, and the
501
+ * **inverse** of react-datepicker's `filterTime` (which returns `true` to *keep* a slot).
502
+ * Use cases: business hours, lunch breaks, blackout slots. Hours are disabled only when the
503
+ * predicate returns `true` for every step within the hour. Always receives 24-hour values.
504
+ */
505
+ filterTime?: (hours: number, minutes: number) => boolean;
498
506
  /** Override ARIA labels (defaults to English) */
499
507
  labels?: Partial<TimePickerLabels>;
500
508
  /** Child components */
501
509
  children: ReactNode;
502
510
  }
503
- declare function TimePickerRoot({ value: controlledValue, defaultValue, onChange, format, step, withSeconds, displayTimezone, disabled, readOnly, labels: labelsProp, children, }: TimePickerRootProps): react_jsx_runtime.JSX.Element;
511
+ declare function TimePickerRoot({ value: controlledValue, defaultValue, onChange, format, step, withSeconds, displayTimezone, disabled, readOnly, filterTime, labels: labelsProp, children, }: TimePickerRootProps): react_jsx_runtime.JSX.Element;
504
512
 
505
513
  interface TimePickerHourListClassNames {
506
514
  root?: string;
package/dist/index.js CHANGED
@@ -1791,6 +1791,7 @@ function TimePickerRoot({
1791
1791
  displayTimezone,
1792
1792
  disabled = false,
1793
1793
  readOnly = false,
1794
+ filterTime,
1794
1795
  labels: labelsProp,
1795
1796
  children
1796
1797
  }) {
@@ -1832,7 +1833,8 @@ function TimePickerRoot({
1832
1833
  isReadOnly: readOnly,
1833
1834
  currentTime,
1834
1835
  pickerId,
1835
- labels: mergedLabels
1836
+ labels: mergedLabels,
1837
+ filterTime
1836
1838
  }),
1837
1839
  [
1838
1840
  currentValue,
@@ -1845,7 +1847,8 @@ function TimePickerRoot({
1845
1847
  readOnly,
1846
1848
  currentTime,
1847
1849
  pickerId,
1848
- mergedLabels
1850
+ mergedLabels,
1851
+ filterTime
1849
1852
  ]
1850
1853
  );
1851
1854
  return /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: contextValue, children });
@@ -1950,17 +1953,41 @@ function useListboxNavigation({
1950
1953
  }
1951
1954
  function TimePickerHourList({ classNames, ...props }) {
1952
1955
  const ctx = useTimePickerContext("TimePicker.HourList");
1953
- const { format, currentTime, isDisabled, isReadOnly } = ctx;
1956
+ const { format, step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
1954
1957
  const hours = useMemo(() => generateHours(format), [format]);
1955
1958
  const selectedHourDisplay = format === "12h" ? to12Hour(currentTime.hours).hours12 : currentTime.hours;
1956
1959
  const currentPeriod = format === "12h" ? to12Hour(currentTime.hours).period : null;
1960
+ const fullyDisabledHours24 = useMemo(() => {
1961
+ if (!filterTime) return null;
1962
+ const disabled = /* @__PURE__ */ new Set();
1963
+ for (let h = 0; h < 24; h++) {
1964
+ let allRejected = true;
1965
+ for (let m = 0; m < 60; m += step) {
1966
+ if (!filterTime(h, m)) {
1967
+ allRejected = false;
1968
+ break;
1969
+ }
1970
+ }
1971
+ if (allRejected) disabled.add(h);
1972
+ }
1973
+ return disabled;
1974
+ }, [filterTime, step]);
1975
+ const isHourDisabled = useCallback(
1976
+ (hourDisplay) => {
1977
+ if (!fullyDisabledHours24) return false;
1978
+ const hours24 = format === "12h" && currentPeriod ? to24Hour(hourDisplay, currentPeriod) : hourDisplay;
1979
+ return fullyDisabledHours24.has(hours24);
1980
+ },
1981
+ [fullyDisabledHours24, format, currentPeriod]
1982
+ );
1957
1983
  const handleSelect = useCallback(
1958
1984
  (hourDisplay) => {
1959
1985
  if (isDisabled || isReadOnly) return;
1986
+ if (isHourDisabled(hourDisplay)) return;
1960
1987
  const hours24 = format === "12h" && currentPeriod ? to24Hour(hourDisplay, currentPeriod) : hourDisplay;
1961
1988
  ctx.setTime({ hours: hours24 });
1962
1989
  },
1963
- [format, currentPeriod, ctx, isDisabled, isReadOnly]
1990
+ [format, currentPeriod, ctx, isDisabled, isReadOnly, isHourDisabled]
1964
1991
  );
1965
1992
  const { listRef, handleKeyDown } = useListboxNavigation({
1966
1993
  items: hours,
@@ -1978,13 +2005,14 @@ function TimePickerHourList({ classNames, ...props }) {
1978
2005
  ...props,
1979
2006
  children: hours.map((hour) => {
1980
2007
  const isSelected = hour === selectedHourDisplay;
2008
+ const isHourFullyDisabled = isHourDisabled(hour);
1981
2009
  const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
1982
2010
  return /* @__PURE__ */ jsx(
1983
2011
  "li",
1984
2012
  {
1985
2013
  role: "option",
1986
2014
  "aria-selected": isSelected,
1987
- "aria-disabled": isDisabled || void 0,
2015
+ "aria-disabled": isDisabled || isHourFullyDisabled || void 0,
1988
2016
  "aria-label": ctx.labels.hourOption(hour),
1989
2017
  "data-selected": isSelected || void 0,
1990
2018
  tabIndex: isSelected ? 0 : -1,
@@ -2001,14 +2029,22 @@ function TimePickerHourList({ classNames, ...props }) {
2001
2029
  }
2002
2030
  function TimePickerMinuteList({ classNames, ...props }) {
2003
2031
  const ctx = useTimePickerContext("TimePicker.MinuteList");
2004
- const { step, currentTime, isDisabled, isReadOnly } = ctx;
2032
+ const { step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
2005
2033
  const minutes = useMemo(() => generateMinutes(step), [step]);
2034
+ const isMinuteDisabled = useCallback(
2035
+ (minute) => {
2036
+ if (!filterTime) return false;
2037
+ return filterTime(currentTime.hours, minute);
2038
+ },
2039
+ [filterTime, currentTime.hours]
2040
+ );
2006
2041
  const handleSelect = useCallback(
2007
2042
  (minute) => {
2008
2043
  if (isDisabled || isReadOnly) return;
2044
+ if (isMinuteDisabled(minute)) return;
2009
2045
  ctx.setTime({ minutes: minute });
2010
2046
  },
2011
- [ctx, isDisabled, isReadOnly]
2047
+ [ctx, isDisabled, isReadOnly, isMinuteDisabled]
2012
2048
  );
2013
2049
  const { listRef, handleKeyDown } = useListboxNavigation({
2014
2050
  items: minutes,
@@ -2026,13 +2062,14 @@ function TimePickerMinuteList({ classNames, ...props }) {
2026
2062
  ...props,
2027
2063
  children: minutes.map((minute) => {
2028
2064
  const isSelected = minute === currentTime.minutes;
2065
+ const isMinuteFullyDisabled = isMinuteDisabled(minute);
2029
2066
  const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
2030
2067
  return /* @__PURE__ */ jsx(
2031
2068
  "li",
2032
2069
  {
2033
2070
  role: "option",
2034
2071
  "aria-selected": isSelected,
2035
- "aria-disabled": isDisabled || void 0,
2072
+ "aria-disabled": isDisabled || isMinuteFullyDisabled || void 0,
2036
2073
  "aria-label": ctx.labels.minuteOption(minute),
2037
2074
  "data-selected": isSelected || void 0,
2038
2075
  tabIndex: isSelected ? 0 : -1,