@kalyx/react 1.0.0-rc.9 → 1.0.1

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
@@ -1,12 +1,29 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
+ var adapterDateFns = require('@kalyx/adapter-date-fns');
4
5
  var react = require('react');
5
6
  var core = require('@kalyx/core');
6
7
  var jsxRuntime = require('react/jsx-runtime');
7
8
  var react$1 = require('@floating-ui/react');
8
9
 
9
- // src/components/DatePicker/Root.tsx
10
+ // src/index.ts
11
+
12
+ // src/internal/defaultAdapter.ts
13
+ var __defaultAdapter = null;
14
+ function setDefaultAdapter(adapter) {
15
+ __defaultAdapter = adapter;
16
+ }
17
+ function getDefaultAdapter() {
18
+ return __defaultAdapter;
19
+ }
20
+ function resolveAdapter(passed, fallback, componentName) {
21
+ if (passed) return passed;
22
+ if (fallback) return fallback;
23
+ throw new Error(
24
+ `[@kalyx/react/headless] ${componentName} requires an adapter. Pass one via <${componentName} adapter={...}>. If you don't need a custom adapter, import from '@kalyx/react' instead.`
25
+ );
26
+ }
10
27
  var DatePickerContext = react.createContext(null);
11
28
  function useDatePickerContext(componentName) {
12
29
  const context = react.useContext(DatePickerContext);
@@ -45,10 +62,11 @@ function DatePickerRoot({
45
62
  displayFormat = "yyyy-MM-dd",
46
63
  locale = "en-US",
47
64
  displayTimezone,
48
- adapter = core.DateFnsAdapter,
65
+ adapter: adapterProp,
49
66
  labels: labelsProp,
50
67
  children
51
68
  }) {
69
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "DatePicker");
52
70
  const pickerId = react.useId();
53
71
  const isControlled = react.useRef(controlledValue !== void 0).current;
54
72
  const referenceRef = react.useRef(null);
@@ -157,6 +175,10 @@ var DatePickerInput = react.forwardRef(
157
175
  const displayFormat = formatProp ?? ctx.displayFormat;
158
176
  const [inputText, setInputText] = react.useState(null);
159
177
  const isComposingRef = react.useRef(false);
178
+ react.useEffect(() => {
179
+ if (isComposingRef.current) return;
180
+ setInputText(null);
181
+ }, [ctx.value]);
160
182
  let formattedValue = "";
161
183
  if (ctx.value) {
162
184
  try {
@@ -1099,6 +1121,17 @@ function useRangePickerContext(componentName) {
1099
1121
  return context;
1100
1122
  }
1101
1123
  var EMPTY_RANGE = { start: null, end: null };
1124
+ var SR_ONLY = {
1125
+ position: "absolute",
1126
+ width: 1,
1127
+ height: 1,
1128
+ padding: 0,
1129
+ margin: -1,
1130
+ overflow: "hidden",
1131
+ clip: "rect(0, 0, 0, 0)",
1132
+ whiteSpace: "nowrap",
1133
+ border: 0
1134
+ };
1102
1135
  function RangePickerRoot({
1103
1136
  value: controlledValue,
1104
1137
  defaultValue,
@@ -1111,10 +1144,11 @@ function RangePickerRoot({
1111
1144
  displayFormat = "yyyy-MM-dd",
1112
1145
  locale = "en-US",
1113
1146
  displayTimezone,
1114
- adapter = core.DateFnsAdapter,
1147
+ adapter: adapterProp,
1115
1148
  labels: labelsProp,
1116
1149
  children
1117
1150
  }) {
1151
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "RangePicker");
1118
1152
  const pickerId = react.useId();
1119
1153
  const isControlled = react.useRef(controlledValue !== void 0).current;
1120
1154
  const referenceRef = react.useRef(null);
@@ -1125,6 +1159,8 @@ function RangePickerRoot({
1125
1159
  const [isOpen, setIsOpen] = react.useState(false);
1126
1160
  const [selectingTarget, setSelectingTarget] = react.useState("start");
1127
1161
  const [hoverDate, setHoverDate] = react.useState(null);
1162
+ const [announcement, setAnnouncement] = react.useState("");
1163
+ const announce = react.useCallback((message) => setAnnouncement(message), []);
1128
1164
  const [viewMonth, setViewMonth] = react.useState(
1129
1165
  () => currentValue.start ?? adapter.today(displayTimezone)
1130
1166
  );
@@ -1227,7 +1263,8 @@ function RangePickerRoot({
1227
1263
  isDisabled,
1228
1264
  isReadOnly: readOnly,
1229
1265
  pickerId,
1230
- labels: mergedLabels
1266
+ labels: mergedLabels,
1267
+ announce
1231
1268
  }),
1232
1269
  [
1233
1270
  currentValue,
@@ -1250,10 +1287,14 @@ function RangePickerRoot({
1250
1287
  isDisabled,
1251
1288
  readOnly,
1252
1289
  pickerId,
1253
- mergedLabels
1290
+ mergedLabels,
1291
+ announce
1254
1292
  ]
1255
1293
  );
1256
- return /* @__PURE__ */ jsxRuntime.jsx(RangePickerContext.Provider, { value: contextValue, children });
1294
+ return /* @__PURE__ */ jsxRuntime.jsxs(RangePickerContext.Provider, { value: contextValue, children: [
1295
+ children,
1296
+ /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: SR_ONLY, children: announcement })
1297
+ ] });
1257
1298
  }
1258
1299
  var RangePickerInput = react.forwardRef(
1259
1300
  function RangePickerInput2({ part, format: formatProp, onClick, onKeyDown, ...props }, ref) {
@@ -1353,17 +1394,6 @@ function safeFormatFullDate2(iso, locale) {
1353
1394
  return iso;
1354
1395
  }
1355
1396
  }
1356
- var srOnly2 = {
1357
- position: "absolute",
1358
- width: "1px",
1359
- height: "1px",
1360
- padding: 0,
1361
- margin: "-1px",
1362
- overflow: "hidden",
1363
- clip: "rect(0, 0, 0, 0)",
1364
- whiteSpace: "nowrap",
1365
- border: 0
1366
- };
1367
1397
  function RangePickerCalendar({
1368
1398
  classNames,
1369
1399
  selectionMode = "range",
@@ -1373,7 +1403,6 @@ function RangePickerCalendar({
1373
1403
  }) {
1374
1404
  const ctx = useRangePickerContext("RangePicker.Calendar");
1375
1405
  const gridRef = react.useRef(null);
1376
- const [announcement, setAnnouncement] = react.useState("");
1377
1406
  const {
1378
1407
  adapter,
1379
1408
  viewMonth,
@@ -1425,7 +1454,7 @@ function RangePickerCalendar({
1425
1454
  ctx.setFocusedDate(adapter.startOfMonth(newMonth));
1426
1455
  const y = adapter.getYear(newMonth);
1427
1456
  const m = adapter.getMonth(newMonth);
1428
- setAnnouncement(core.formatMonthYear(y, m, locale));
1457
+ ctx.announce(core.formatMonthYear(y, m, locale));
1429
1458
  },
1430
1459
  [adapter, viewMonth, ctx, locale]
1431
1460
  );
@@ -1437,15 +1466,27 @@ function RangePickerCalendar({
1437
1466
  const range = { start: weekStart, end: weekEnd };
1438
1467
  ctx.setRange(range);
1439
1468
  ctx.close();
1440
- setAnnouncement(
1441
- `${safeFormatFullDate2(weekStart, locale)} \u2013 ${safeFormatFullDate2(weekEnd, locale)}`
1469
+ ctx.announce(
1470
+ `${ctx.labels.rangeSelected}: ${safeFormatFullDate2(weekStart, locale)} \u2013 ${safeFormatFullDate2(weekEnd, locale)}`
1442
1471
  );
1443
1472
  } else {
1473
+ const wasPickingStart = selectingTarget === "start";
1474
+ const previousStart = value.start;
1444
1475
  ctx.selectDate(iso);
1445
- setAnnouncement(safeFormatFullDate2(iso, locale));
1476
+ const formatted = safeFormatFullDate2(iso, locale);
1477
+ if (wasPickingStart) {
1478
+ ctx.announce(`${formatted}. ${ctx.labels.selectingEnd}`);
1479
+ } else if (previousStart) {
1480
+ const [start, end] = adapter.isBefore(iso, previousStart) ? [iso, previousStart] : [previousStart, iso];
1481
+ ctx.announce(
1482
+ `${ctx.labels.rangeSelected}: ${safeFormatFullDate2(start, locale)} \u2013 ${safeFormatFullDate2(end, locale)}`
1483
+ );
1484
+ } else {
1485
+ ctx.announce(formatted);
1486
+ }
1446
1487
  }
1447
1488
  },
1448
- [selectionMode, adapter, weekStartsOn, ctx, locale]
1489
+ [selectionMode, adapter, weekStartsOn, ctx, locale, selectingTarget, value.start]
1449
1490
  );
1450
1491
  const handleDayClick = react.useCallback(
1451
1492
  (day) => {
@@ -1654,8 +1695,7 @@ function RangePickerCalendar({
1654
1695
  )) })
1655
1696
  ]
1656
1697
  }
1657
- ),
1658
- /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: srOnly2, children: announcement })
1698
+ )
1659
1699
  ] });
1660
1700
  }
1661
1701
  function RangePickerPresets({ classNames, children, ...props }) {
@@ -1713,35 +1753,28 @@ function RangePickerPreset({
1713
1753
  ...props
1714
1754
  }) {
1715
1755
  const ctx = useRangePickerContext("RangePicker.Preset");
1756
+ const resolved = react.useMemo(() => {
1757
+ if (directRange) return directRange;
1758
+ if (presetKey)
1759
+ return resolvePreset(presetKey, ctx.adapter.today(ctx.displayTimezone), ctx.adapter);
1760
+ return null;
1761
+ }, [directRange, presetKey, ctx.adapter, ctx.displayTimezone]);
1716
1762
  const handleClick = react.useCallback(
1717
1763
  (e) => {
1718
1764
  if (ctx.isDisabled || ctx.isReadOnly) return;
1719
- let resolved;
1720
- if (directRange) {
1721
- resolved = directRange;
1722
- } else if (presetKey) {
1723
- resolved = resolvePreset(presetKey, ctx.adapter.today(), ctx.adapter);
1724
- } else {
1725
- return;
1726
- }
1765
+ if (!resolved) return;
1727
1766
  ctx.setRange(resolved);
1728
1767
  ctx.close();
1729
1768
  onClick?.(e);
1730
1769
  },
1731
- [ctx, presetKey, directRange, onClick]
1770
+ [ctx, resolved, onClick]
1732
1771
  );
1733
- const isActive = (() => {
1734
- if (!ctx.value.start || !ctx.value.end) return false;
1735
- let target;
1736
- if (directRange) {
1737
- target = directRange;
1738
- } else if (presetKey) {
1739
- target = resolvePreset(presetKey, ctx.adapter.today(), ctx.adapter);
1740
- } else {
1772
+ const isActive = react.useMemo(() => {
1773
+ if (!ctx.value.start || !ctx.value.end || !resolved || !resolved.start || !resolved.end) {
1741
1774
  return false;
1742
1775
  }
1743
- return target.start !== null && target.end !== null && ctx.adapter.isSameDay(ctx.value.start, target.start) && ctx.adapter.isSameDay(ctx.value.end, target.end);
1744
- })();
1776
+ return ctx.adapter.isSameDay(ctx.value.start, resolved.start) && ctx.adapter.isSameDay(ctx.value.end, resolved.end);
1777
+ }, [ctx.value.start, ctx.value.end, ctx.adapter, resolved]);
1745
1778
  return /* @__PURE__ */ jsxRuntime.jsx(
1746
1779
  "button",
1747
1780
  {
@@ -1779,9 +1812,6 @@ function useTimePickerContext(componentName) {
1779
1812
  }
1780
1813
  return context;
1781
1814
  }
1782
- function getDefaultIso() {
1783
- return core.DateFnsAdapter.today();
1784
- }
1785
1815
  function TimePickerRoot({
1786
1816
  value: controlledValue,
1787
1817
  defaultValue,
@@ -1793,9 +1823,11 @@ function TimePickerRoot({
1793
1823
  disabled = false,
1794
1824
  readOnly = false,
1795
1825
  filterTime,
1826
+ adapter: adapterProp,
1796
1827
  labels: labelsProp,
1797
1828
  children
1798
1829
  }) {
1830
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "TimePicker");
1799
1831
  const pickerId = react.useId();
1800
1832
  const mergedLabels = react.useMemo(
1801
1833
  () => ({ ...core.DEFAULT_TIMEPICKER_LABELS, ...labelsProp }),
@@ -1806,21 +1838,21 @@ function TimePickerRoot({
1806
1838
  defaultValue ?? null
1807
1839
  );
1808
1840
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1809
- const baseIso = currentValue ?? getDefaultIso();
1810
- const currentTime = react.useMemo(
1811
- () => displayTimezone ? core.getTimeInTimezone(baseIso, displayTimezone) : core.getTime(baseIso),
1812
- [baseIso, displayTimezone]
1813
- );
1841
+ const currentTime = react.useMemo(() => {
1842
+ if (!currentValue) return { hours: 0, minutes: 0, seconds: 0 };
1843
+ return displayTimezone ? core.getTimeInTimezone(currentValue, displayTimezone) : core.getTime(currentValue);
1844
+ }, [currentValue, displayTimezone]);
1814
1845
  const setTime = react.useCallback(
1815
1846
  (partial) => {
1816
1847
  if (disabled || readOnly) return;
1817
- const newIso = displayTimezone ? core.setTimeInTimezone(baseIso, partial, displayTimezone) : core.setTime(baseIso, partial);
1848
+ const base = currentValue ?? adapter.today(displayTimezone);
1849
+ const newIso = displayTimezone ? core.setTimeInTimezone(base, partial, displayTimezone) : core.setTime(base, partial);
1818
1850
  if (!isControlled) {
1819
1851
  setUncontrolledValue(newIso);
1820
1852
  }
1821
1853
  onChange?.(newIso);
1822
1854
  },
1823
- [disabled, readOnly, baseIso, isControlled, onChange, displayTimezone]
1855
+ [disabled, readOnly, currentValue, displayTimezone, isControlled, onChange, adapter]
1824
1856
  );
1825
1857
  const contextValue = react.useMemo(
1826
1858
  () => ({
@@ -1858,6 +1890,9 @@ var TimePickerInput = react.forwardRef(
1858
1890
  function TimePickerInput2({ onBlur, onKeyDown, ...props }, ref) {
1859
1891
  const ctx = useTimePickerContext("TimePicker.Input");
1860
1892
  const [inputText, setInputText] = react.useState(null);
1893
+ react.useEffect(() => {
1894
+ setInputText(null);
1895
+ }, [ctx.value]);
1861
1896
  const displayValue = inputText !== null ? inputText : core.formatTimeString(ctx.currentTime, ctx.withSeconds);
1862
1897
  const commitInput = react.useCallback(() => {
1863
1898
  if (inputText === null) return;
@@ -2177,9 +2212,6 @@ var TimePicker = Object.assign(TimePickerRoot, {
2177
2212
  MinuteList: TimePickerMinuteList,
2178
2213
  AmPmToggle: TimePickerAmPmToggle
2179
2214
  });
2180
- function getDefaultIso2() {
2181
- return core.DateFnsAdapter.today();
2182
- }
2183
2215
  function DateTimePickerRoot({
2184
2216
  value: controlledValue,
2185
2217
  defaultValue,
@@ -2188,16 +2220,19 @@ function DateTimePickerRoot({
2188
2220
  onCalendarNavigate,
2189
2221
  format = "24h",
2190
2222
  step = 1,
2223
+ withSeconds = false,
2224
+ filterTime,
2191
2225
  disabled = false,
2192
2226
  readOnly = false,
2193
2227
  weekStartsOn = 0,
2194
2228
  displayFormat = "yyyy-MM-dd HH:mm",
2195
2229
  locale = "en-US",
2196
2230
  displayTimezone,
2197
- adapter = core.DateFnsAdapter,
2231
+ adapter: adapterProp,
2198
2232
  labels: labelsProp,
2199
2233
  children
2200
2234
  }) {
2235
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "DateTimePicker");
2201
2236
  const pickerId = react.useId();
2202
2237
  const mergedDateLabels = react.useMemo(
2203
2238
  () => ({ ...core.DEFAULT_DATEPICKER_LABELS, ...labelsProp }),
@@ -2228,11 +2263,10 @@ function DateTimePickerRoot({
2228
2263
  () => Array.isArray(disabled) ? disabled : [],
2229
2264
  [disabled]
2230
2265
  );
2231
- const baseIso = currentValue ?? getDefaultIso2();
2232
- const currentTime = react.useMemo(
2233
- () => displayTimezone ? core.getTimeInTimezone(baseIso, displayTimezone) : core.getTime(baseIso),
2234
- [baseIso, displayTimezone]
2235
- );
2266
+ const currentTime = react.useMemo(() => {
2267
+ if (!currentValue) return { hours: 0, minutes: 0, seconds: 0 };
2268
+ return displayTimezone ? core.getTimeInTimezone(currentValue, displayTimezone) : core.getTime(currentValue);
2269
+ }, [currentValue, displayTimezone]);
2236
2270
  const updateValue = react.useCallback(
2237
2271
  (next) => {
2238
2272
  if (isDisabled || readOnly) return;
@@ -2258,11 +2292,11 @@ function DateTimePickerRoot({
2258
2292
  );
2259
2293
  const setTime = react.useCallback(
2260
2294
  (partial) => {
2261
- const base = currentValue ?? getDefaultIso2();
2295
+ const base = currentValue ?? adapter.today(displayTimezone);
2262
2296
  const merged = displayTimezone ? core.setTimeInTimezone(base, partial, displayTimezone) : core.setTime(base, partial);
2263
2297
  updateValue(merged);
2264
2298
  },
2265
- [currentValue, updateValue, displayTimezone]
2299
+ [currentValue, updateValue, displayTimezone, adapter]
2266
2300
  );
2267
2301
  const open = react.useCallback(() => {
2268
2302
  if (isDisabled || readOnly) return;
@@ -2329,25 +2363,28 @@ function DateTimePickerRoot({
2329
2363
  setTime,
2330
2364
  format,
2331
2365
  step,
2332
- withSeconds: false,
2366
+ withSeconds,
2333
2367
  displayTimezone,
2334
2368
  isDisabled,
2335
2369
  isReadOnly: readOnly,
2336
2370
  currentTime,
2337
2371
  pickerId,
2338
- labels: mergedTimeLabels
2372
+ labels: mergedTimeLabels,
2373
+ filterTime
2339
2374
  }),
2340
2375
  [
2341
2376
  currentValue,
2342
2377
  setTime,
2343
2378
  format,
2344
2379
  step,
2380
+ withSeconds,
2345
2381
  displayTimezone,
2346
2382
  isDisabled,
2347
2383
  readOnly,
2348
2384
  currentTime,
2349
2385
  pickerId,
2350
- mergedTimeLabels
2386
+ mergedTimeLabels,
2387
+ filterTime
2351
2388
  ]
2352
2389
  );
2353
2390
  return /* @__PURE__ */ jsxRuntime.jsx(DatePickerContext.Provider, { value: dateContext, children: /* @__PURE__ */ jsxRuntime.jsx(TimePickerContext.Provider, { value: timeContext, children }) });
@@ -2730,9 +2767,10 @@ function useDatePicker(options = {}) {
2730
2767
  onChange,
2731
2768
  disabled = [],
2732
2769
  weekStartsOn = 0,
2733
- adapter = core.DateFnsAdapter,
2770
+ adapter: adapterProp,
2734
2771
  displayTimezone
2735
2772
  } = options;
2773
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "useDatePicker");
2736
2774
  const pickerId = react.useId();
2737
2775
  const isControlled = react.useRef(controlledValue !== void 0).current;
2738
2776
  const [uncontrolledValue, setUncontrolledValue] = react.useState(
@@ -2813,9 +2851,10 @@ function useRangePicker(options = {}) {
2813
2851
  onChange,
2814
2852
  disabled = [],
2815
2853
  weekStartsOn = 0,
2816
- adapter = core.DateFnsAdapter,
2854
+ adapter: adapterProp,
2817
2855
  displayTimezone
2818
2856
  } = options;
2857
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "useRangePicker");
2819
2858
  const pickerId = react.useId();
2820
2859
  const isControlled = react.useRef(controlledValue !== void 0).current;
2821
2860
  const [uncontrolledValue, setUncontrolledValue] = react.useState(
@@ -2920,9 +2959,6 @@ function useRangePicker(options = {}) {
2920
2959
  adapter
2921
2960
  };
2922
2961
  }
2923
- function getDefaultIso3() {
2924
- return core.DateFnsAdapter.today();
2925
- }
2926
2962
  function useTimePicker(options = {}) {
2927
2963
  const {
2928
2964
  value: controlledValue,
@@ -2930,15 +2966,17 @@ function useTimePicker(options = {}) {
2930
2966
  onChange,
2931
2967
  format = "24h",
2932
2968
  step = 1,
2933
- displayTimezone
2969
+ displayTimezone,
2970
+ adapter: adapterProp
2934
2971
  } = options;
2972
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "useTimePicker");
2935
2973
  const pickerId = react.useId();
2936
2974
  const isControlled = react.useRef(controlledValue !== void 0).current;
2937
2975
  const [uncontrolledValue, setUncontrolledValue] = react.useState(
2938
2976
  defaultValue ?? null
2939
2977
  );
2940
2978
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
2941
- const baseIso = currentValue ?? getDefaultIso3();
2979
+ const baseIso = currentValue ?? adapter.today();
2942
2980
  const currentTime = react.useMemo(
2943
2981
  () => displayTimezone ? core.getTimeInTimezone(baseIso, displayTimezone) : core.getTime(baseIso),
2944
2982
  [baseIso, displayTimezone]
@@ -2988,10 +3026,27 @@ function useTimePicker(options = {}) {
2988
3026
  pickerId
2989
3027
  };
2990
3028
  }
3029
+ setDefaultAdapter(adapterDateFns.DateFnsAdapter);
2991
3030
 
2992
3031
  Object.defineProperty(exports, "DateFnsAdapter", {
2993
3032
  enumerable: true,
2994
- get: function () { return core.DateFnsAdapter; }
3033
+ get: function () { return adapterDateFns.DateFnsAdapter; }
3034
+ });
3035
+ Object.defineProperty(exports, "DEFAULT_DATEPICKER_LABELS", {
3036
+ enumerable: true,
3037
+ get: function () { return core.DEFAULT_DATEPICKER_LABELS; }
3038
+ });
3039
+ Object.defineProperty(exports, "DEFAULT_DATETIMEPICKER_LABELS", {
3040
+ enumerable: true,
3041
+ get: function () { return core.DEFAULT_DATETIMEPICKER_LABELS; }
3042
+ });
3043
+ Object.defineProperty(exports, "DEFAULT_RANGEPICKER_LABELS", {
3044
+ enumerable: true,
3045
+ get: function () { return core.DEFAULT_RANGEPICKER_LABELS; }
3046
+ });
3047
+ Object.defineProperty(exports, "DEFAULT_TIMEPICKER_LABELS", {
3048
+ enumerable: true,
3049
+ get: function () { return core.DEFAULT_TIMEPICKER_LABELS; }
2995
3050
  });
2996
3051
  exports.DatePicker = DatePicker;
2997
3052
  exports.DateTimePicker = DateTimePicker;