@kalyx/core 1.0.0-rc.0 → 1.0.0-rc.2

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 ADDED
@@ -0,0 +1,125 @@
1
+ # @kalyx/core
2
+
3
+ ## 1.0.0-rc.2
4
+
5
+ ### Patch Changes
6
+
7
+ - aadb512: Security: pin transitive `postcss` to `>=8.5.10` via `pnpm.overrides`.
8
+
9
+ Two `postcss` versions in `pnpm-lock.yaml` (`8.4.31` from a `postcss-load-config` chain and `8.5.9` from the `tsup` chain) were affected by [GHSA-qx2v-qp2m-jg93](https://osv.dev/GHSA-qx2v-qp2m-jg93) (CVSS 6.1 — improper newline handling that lets crafted input bypass quote escapes). Both are now resolved to `8.5.10`+. The OSV scanner workflow (which auto-creates issues #23 / #24 / #27) now reports zero advisories.
10
+
11
+ - 21f3c1f: Resolve v1.0-rc release-blocking defects (P0):
12
+ - **`"use client"` directive** — bundle is now marked as a React Server Component client boundary via tsup banner. Next.js App Router consumers no longer have to wrap each import.
13
+ - **Stable `today()`/`now()` initialization** — `viewMonth`/`focusedDate` `useState` calls in `DatePicker`/`RangePicker`/`DateTimePicker` Roots now use lazy initializers, so the adapter isn't called on every render.
14
+ - **`@kalyx/core` version sync** — bumped from `1.0.0-rc.0` to `1.0.0-rc.1` to match `@kalyx/react`.
15
+ - **`@kalyx/core` package contents** — `LICENSE` and `CHANGELOG.md` are now included in the npm tarball (`files` field).
16
+ - **Form auto-submit blocked when calendar open** — pressing Enter inside `DatePicker.Input`/`RangePicker.Input`/`DateTimePicker.Input` while the popover is open no longer submits the surrounding `<form>`.
17
+ - **`aria-haspopup="dialog"` on Trigger** — completes the WAI-ARIA combobox/dialog pattern.
18
+ - **Disabled cells skipped during keyboard navigation** — Calendar arrow keys / PageUp/Down / Home / End now step over disabled days and stop only when no enabled day is reachable.
19
+
20
+ - 3228533: P1 v1.0-rc API/a11y/docs improvements:
21
+ - **Popover focus-out close** — `usePopover` now closes the popover when focus leaves the floating layer and the reference element (Tab through). Matches the Radix/Ark dismissable layer pattern.
22
+ - **`name` prop + hidden form input** — `DatePicker.Input` accepts a `name` prop. When set, a hidden `<input type="hidden">` is rendered alongside the visible input so the value participates in native form submission and integrates with `react-hook-form` Controller-less flows.
23
+ - **IME composition handling** — `DatePicker.Input` now defers parsing during IME composition (`compositionstart` / `compositionend`). Previously, partial Korean / Japanese / Chinese input was repeatedly re-parsed and the user's text disappeared.
24
+ - **README parity** — Korean README now has the "Styling with Tailwind CSS" and "Using data attributes" sections that were missing. Version table and bundle-size claim corrected to `v1.0.0-rc.1` / `11.57 KB`.
25
+ - **Package metadata** — `peerDependenciesMeta`, `engines.node`, and `publishConfig.provenance` added to both `@kalyx/react` and `@kalyx/core`.
26
+ - **`@kalyx/react` description** — corrected from "under 10 KB gzipped" (false claim) to "≤12 KB gzipped".
27
+
28
+ - b6129ed: P2 polish for v1.0-rc:
29
+ - **Calendar grid `aria-rowindex` / `aria-colindex` / `aria-rowcount` / `aria-colcount`** — `DatePicker.Calendar` and `RangePicker.Calendar` now expose grid coordinates so screenreaders announce position ("row 3 of 6, column 4 of 7") during keyboard navigation.
30
+ - **`displayName` on all `forwardRef` components** — `DatePicker.Input`, `DatePicker.Trigger`, `RangePicker.Input`, `TimePicker.Input`, `DateTimePicker.Input` now render with their public dot-notation name in React DevTools.
31
+ - **JSDoc on `DatePicker.Input` and `DatePicker.Trigger`** — public API surface for the most-used components has explanatory docstrings.
32
+ - **`addYears` leap-day regression tests** — locked the date-fns clamp behavior (2024-02-29 + 1y → 2025-02-28, not March 1).
33
+ - **DST fall-back ambiguous-hour regression test** — captures the current behavior of `setTimeInTimezone` for 2026-11-01 01:30 America/New_York so silent drift surfaces as a test failure.
34
+ - **Test count claim corrected** — root and core `CLAUDE.md` previously claimed "1,000+ unit tests"; actual count is ~140 in core, 374 across the workspace.
35
+
36
+ ## 1.0.0-rc.0
37
+
38
+ ### Major Changes
39
+
40
+ - ca7180e: chore: v1.0 milestone — API freeze.
41
+
42
+ Kalyx v1.0 declares the public API stable. This is a milestone release bundling the v0.5 surface additions (MonthPicker, YearPicker, WeekPicker, DatePicker.Presets, `onOpenChange`/`onCalendarNavigate` event callbacks) with an explicit commitment to semantic versioning going forward.
43
+
44
+ ### What v1.0 commits to
45
+ - **Public API surface** — exports from `@kalyx/react` and `@kalyx/core` listed in their `index.ts` files. Any breaking change requires a major bump.
46
+ - **Compositional structure** — Root + subcomponent names (`DatePicker.Input`, `DatePicker.Calendar`, …) are stable. Removal or renaming requires a major bump.
47
+ - **Value semantics** — ISO 8601 UTC strings for single dates, `DateRange` `{start, end}` for ranges. `displayTimezone` behavior (civil-midnight-in-tz for date selection) is stable.
48
+ - **Accessibility contracts** — role/aria-\* attributes emitted by each component are stable.
49
+
50
+ ### What v1.0 does NOT freeze
51
+ - Internal implementation details (non-exported functions, component file layout).
52
+ - CSS class name strings on elements — no classes are applied by default; only when a consumer passes them via `classNames` props.
53
+ - Error message text.
54
+ - Peer dependency version ranges (may expand to cover new React majors).
55
+
56
+ ### Breaking changes vs 0.4.x
57
+
58
+ None. v1.0 is API-compatible with 0.4.x — existing code continues to work. The major bump communicates stability commitment, not breakage.
59
+
60
+ ## 0.4.0
61
+
62
+ ### Minor Changes
63
+
64
+ - 104bbf2: feat: full `displayTimezone` support across all pickers (v0.4)
65
+
66
+ All four pickers (`DatePicker`, `RangePicker`, `TimePicker`, `DateTimePicker`) and their corresponding hooks (`useDatePicker`, `useRangePicker`, `useTimePicker`) now accept a `displayTimezone` prop/option.
67
+
68
+ When set, the value stored via `onChange` is the **civil midnight of the selected day in the target timezone** (in UTC-ISO form), eliminating the classic "day off by one" bug that affects picker libraries bound to `new Date()`. Input formatting, calendar highlighting, and the time-of-day controls all follow the display timezone — including DST-aware offsets for zones like `America/New_York` and `Europe/London`.
69
+
70
+ `DateFnsAdapter` now honors the `timezone` argument on `format`, `isSameDay`, `startOfDay`, and `today` (previously declared-but-ignored). Core also exposes new helpers:
71
+ - `civilMidnightFromUtcDay(iso, tz)`
72
+ - `getTimeInTimezone(iso, tz)`
73
+ - `setTimeInTimezone(iso, partial, tz)`
74
+
75
+ No breaking changes — omitting `displayTimezone` keeps the existing UTC semantics.
76
+
77
+ ### Patch Changes
78
+
79
+ - b3a8897: perf: mark `@kalyx/core` as `sideEffects: false` so downstream bundlers can tree-shake unused exports. Safe because the package is purely functional (no module-level side effects).
80
+
81
+ ## 0.3.0
82
+
83
+ ### Minor Changes
84
+
85
+ - 669391b: Improve code quality, performance, and stability
86
+ - Enforce UTC timezone suffix in ISO regex
87
+ - Extract shared usePopover and useListboxNavigation hooks
88
+ - Add Intl.DateTimeFormat caching for locale/timezone utilities
89
+ - Memoize disabledRules to prevent unnecessary context re-creation
90
+ - Add try-catch around adapter.format() for error resilience
91
+ - Cancel requestAnimationFrame on unmount in listbox navigation
92
+ - Remove unused parseInputValue format parameter
93
+ - Boost test coverage: 87% → 92%
94
+ - Fix bundle size measurement to report both ESM and CJS
95
+
96
+ ## 0.2.2
97
+
98
+ ### Patch Changes
99
+
100
+ - ebf4fd7: Add repository/homepage/bugs/keywords metadata to @kalyx/core for npm provenance validation
101
+
102
+ ## 0.2.1
103
+
104
+ ### Patch Changes
105
+
106
+ - fe0e63e: Add full documentation site (Docusaurus, EN/KO), rewrite READMEs for npm, fix CI pnpm version to 10
107
+
108
+ ## 0.2.0
109
+
110
+ ### Minor Changes
111
+
112
+ - e9bb9e8: Initial release of Kalyx — headless, SSR-safe React DatePicker library.
113
+
114
+ Features:
115
+ - DatePicker: single date selection with Calendar, Input, Trigger, Popover
116
+ - RangePicker: date range selection with auto-swap and hover preview
117
+ - TimePicker: 12h/24h mode, minute step, HourList/MinuteList/AmPmToggle
118
+ - DateTimePicker: combined date+time via context bridging (reuses existing components)
119
+ - useDatePicker, useRangePicker, useTimePicker hooks for custom UIs
120
+ - WAI-ARIA compliant: grid, dialog, combobox, listbox, radiogroup patterns
121
+ - SSR safe: verified with Next.js 15 App Router
122
+ - Zero CSS: style with classNames prop and data-\* attributes
123
+ - ISO 8601 UTC strings only (no native Date objects)
124
+ - Bundle: 7.71KB gzip (target ≤12KB)
125
+ - 185 unit/integration tests passing
package/README.md CHANGED
@@ -73,6 +73,36 @@ import {
73
73
  } from '@kalyx/core';
74
74
  ```
75
75
 
76
+ ### Timezone helpers
77
+
78
+ DST-aware timezone utilities used by every picker when `displayTimezone` is set.
79
+
80
+ ```ts
81
+ import {
82
+ formatInTimezone,
83
+ startOfDayInTimezone,
84
+ isSameDayInTimezone,
85
+ todayInTimezone,
86
+ getTimezoneOffsetMinutes,
87
+ civilMidnightFromUtcDay,
88
+ getTimeInTimezone,
89
+ setTimeInTimezone,
90
+ } from '@kalyx/core';
91
+ ```
92
+
93
+ ### Accessibility labels
94
+
95
+ Default ARIA labels (English). Override via the `labels` prop on any picker Root.
96
+
97
+ ```ts
98
+ import {
99
+ DEFAULT_DATEPICKER_LABELS,
100
+ DEFAULT_RANGEPICKER_LABELS,
101
+ DEFAULT_TIMEPICKER_LABELS,
102
+ DEFAULT_DATETIMEPICKER_LABELS,
103
+ } from '@kalyx/core';
104
+ ```
105
+
76
106
  ## Principles
77
107
 
78
108
  - **All dates are ISO 8601 UTC strings** — never `Date` objects.
package/dist/index.cjs CHANGED
@@ -82,9 +82,7 @@ function getCachedPartsFormatter(timeZone) {
82
82
  }
83
83
  function partsInTimezone(utc, timeZone) {
84
84
  const dtf = getCachedPartsFormatter(timeZone);
85
- const parts = Object.fromEntries(
86
- dtf.formatToParts(utc).map((p) => [p.type, p.value])
87
- );
85
+ const parts = Object.fromEntries(dtf.formatToParts(utc).map((p) => [p.type, p.value]));
88
86
  return {
89
87
  year: Number(parts.year),
90
88
  month: Number(parts.month),
@@ -135,14 +133,9 @@ function todayInTimezone(timeZone) {
135
133
  }
136
134
  function civilMidnightFromUtcDay(gridUtcIso, timeZone) {
137
135
  const utc = (0, import_date_fns.parseISO)(gridUtcIso);
138
- const probe = new Date(Date.UTC(
139
- utc.getUTCFullYear(),
140
- utc.getUTCMonth(),
141
- utc.getUTCDate(),
142
- 12,
143
- 0,
144
- 0
145
- )).toISOString();
136
+ const probe = new Date(
137
+ Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate(), 12, 0, 0)
138
+ ).toISOString();
146
139
  return startOfDayInTimezone(probe, timeZone);
147
140
  }
148
141
  function getTimeInTimezone(iso, timeZone) {
@@ -154,7 +147,14 @@ function setTimeInTimezone(iso, partial, timeZone) {
154
147
  const targetHours = partial.hours ?? p.hour;
155
148
  const targetMinutes = partial.minutes ?? p.minute;
156
149
  const targetSeconds = partial.seconds ?? p.second;
157
- const civilEpoch = Date.UTC(p.year, p.month - 1, p.day, targetHours, targetMinutes, targetSeconds);
150
+ const civilEpoch = Date.UTC(
151
+ p.year,
152
+ p.month - 1,
153
+ p.day,
154
+ targetHours,
155
+ targetMinutes,
156
+ targetSeconds
157
+ );
158
158
  const probe1 = new Date(civilEpoch).toISOString();
159
159
  const offset1 = getTimezoneOffsetMinutes(probe1, timeZone);
160
160
  const realEpoch1 = civilEpoch - offset1 * 6e4;
@@ -193,15 +193,9 @@ function utcStartOfWeek(d, weekStartsOn) {
193
193
  }
194
194
  function utcEndOfWeek(d, weekStartsOn) {
195
195
  const start = utcStartOfWeek(d, weekStartsOn);
196
- return new Date(Date.UTC(
197
- start.getUTCFullYear(),
198
- start.getUTCMonth(),
199
- start.getUTCDate() + 6,
200
- 23,
201
- 59,
202
- 59,
203
- 999
204
- ));
196
+ return new Date(
197
+ Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate() + 6, 23, 59, 59, 999)
198
+ );
205
199
  }
206
200
  var DateFnsAdapter = {
207
201
  parse(value) {
package/dist/index.js CHANGED
@@ -32,9 +32,7 @@ function getCachedPartsFormatter(timeZone) {
32
32
  }
33
33
  function partsInTimezone(utc, timeZone) {
34
34
  const dtf = getCachedPartsFormatter(timeZone);
35
- const parts = Object.fromEntries(
36
- dtf.formatToParts(utc).map((p) => [p.type, p.value])
37
- );
35
+ const parts = Object.fromEntries(dtf.formatToParts(utc).map((p) => [p.type, p.value]));
38
36
  return {
39
37
  year: Number(parts.year),
40
38
  month: Number(parts.month),
@@ -85,14 +83,9 @@ function todayInTimezone(timeZone) {
85
83
  }
86
84
  function civilMidnightFromUtcDay(gridUtcIso, timeZone) {
87
85
  const utc = parseISO(gridUtcIso);
88
- const probe = new Date(Date.UTC(
89
- utc.getUTCFullYear(),
90
- utc.getUTCMonth(),
91
- utc.getUTCDate(),
92
- 12,
93
- 0,
94
- 0
95
- )).toISOString();
86
+ const probe = new Date(
87
+ Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate(), 12, 0, 0)
88
+ ).toISOString();
96
89
  return startOfDayInTimezone(probe, timeZone);
97
90
  }
98
91
  function getTimeInTimezone(iso, timeZone) {
@@ -104,7 +97,14 @@ function setTimeInTimezone(iso, partial, timeZone) {
104
97
  const targetHours = partial.hours ?? p.hour;
105
98
  const targetMinutes = partial.minutes ?? p.minute;
106
99
  const targetSeconds = partial.seconds ?? p.second;
107
- const civilEpoch = Date.UTC(p.year, p.month - 1, p.day, targetHours, targetMinutes, targetSeconds);
100
+ const civilEpoch = Date.UTC(
101
+ p.year,
102
+ p.month - 1,
103
+ p.day,
104
+ targetHours,
105
+ targetMinutes,
106
+ targetSeconds
107
+ );
108
108
  const probe1 = new Date(civilEpoch).toISOString();
109
109
  const offset1 = getTimezoneOffsetMinutes(probe1, timeZone);
110
110
  const realEpoch1 = civilEpoch - offset1 * 6e4;
@@ -143,15 +143,9 @@ function utcStartOfWeek(d, weekStartsOn) {
143
143
  }
144
144
  function utcEndOfWeek(d, weekStartsOn) {
145
145
  const start = utcStartOfWeek(d, weekStartsOn);
146
- return new Date(Date.UTC(
147
- start.getUTCFullYear(),
148
- start.getUTCMonth(),
149
- start.getUTCDate() + 6,
150
- 23,
151
- 59,
152
- 59,
153
- 999
154
- ));
146
+ return new Date(
147
+ Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate() + 6, 23, 59, 59, 999)
148
+ );
155
149
  }
156
150
  var DateFnsAdapter = {
157
151
  parse(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kalyx/core",
3
- "version": "1.0.0-rc.0",
3
+ "version": "1.0.0-rc.2",
4
4
  "description": "Kalyx core — platform-agnostic date logic, IANA timezone helpers, and the DateAdapter contract used by @kalyx/react",
5
5
  "license": "MIT",
6
6
  "author": "jiji-hoon96",
@@ -42,12 +42,21 @@
42
42
  }
43
43
  },
44
44
  "files": [
45
- "dist"
45
+ "dist",
46
+ "CHANGELOG.md",
47
+ "LICENSE"
46
48
  ],
47
49
  "dependencies": {
48
50
  "date-fns": "^4.0.0",
49
51
  "date-fns-tz": "^3.0.0"
50
52
  },
53
+ "engines": {
54
+ "node": ">=18.0.0"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public",
58
+ "provenance": true
59
+ },
51
60
  "scripts": {
52
61
  "build": "tsup src/index.ts --format esm,cjs --dts --clean",
53
62
  "typecheck": "tsc --noEmit",