@axenstudio/axen-form 0.1.9 → 0.1.11

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.
Files changed (101) hide show
  1. package/README.md +783 -270
  2. package/dist/core/dateUtils.cjs +7 -3
  3. package/dist/core/dateUtils.d.ts +1 -1
  4. package/dist/core/dateUtils.d.ts.map +1 -1
  5. package/dist/core/dateUtils.js +7 -3
  6. package/dist/core/validation.cjs +4 -1
  7. package/dist/core/validation.d.ts.map +1 -1
  8. package/dist/core/validation.js +4 -1
  9. package/dist/fields/AutocompleteField/AutocompleteField.cjs +32 -32
  10. package/dist/fields/AutocompleteField/AutocompleteField.js +32 -32
  11. package/dist/fields/AutocompleteField/AutocompleteField.module.css.cjs +34 -13
  12. package/dist/fields/AutocompleteField/AutocompleteField.module.css.js +34 -13
  13. package/dist/fields/AutocompleteMultiField/AutocompleteMultiField.cjs +90 -88
  14. package/dist/fields/AutocompleteMultiField/AutocompleteMultiField.d.ts.map +1 -1
  15. package/dist/fields/AutocompleteMultiField/AutocompleteMultiField.js +90 -88
  16. package/dist/fields/AutocompletePredictField/AutocompletePredictField.cjs +81 -64
  17. package/dist/fields/AutocompletePredictField/AutocompletePredictField.d.ts.map +1 -1
  18. package/dist/fields/AutocompletePredictField/AutocompletePredictField.js +81 -64
  19. package/dist/fields/CheckboxField/CheckboxField.module.css.cjs +12 -0
  20. package/dist/fields/CheckboxField/CheckboxField.module.css.js +12 -0
  21. package/dist/fields/ColorField/ColorField.module.css.cjs +12 -0
  22. package/dist/fields/ColorField/ColorField.module.css.js +12 -0
  23. package/dist/fields/DateField/DateField.cjs +56 -4
  24. package/dist/fields/DateField/DateField.d.ts +1 -1
  25. package/dist/fields/DateField/DateField.d.ts.map +1 -1
  26. package/dist/fields/DateField/DateField.js +56 -4
  27. package/dist/fields/DateField/DatePicker.cjs +194 -0
  28. package/dist/fields/DateField/DatePicker.d.ts +11 -0
  29. package/dist/fields/DateField/DatePicker.d.ts.map +1 -0
  30. package/dist/fields/DateField/DatePicker.js +194 -0
  31. package/dist/fields/DateField/DatePicker.module.css.cjs +62 -0
  32. package/dist/fields/DateField/DatePicker.module.css.js +62 -0
  33. package/dist/fields/DateTimeField/DateTimeField.cjs +126 -8
  34. package/dist/fields/DateTimeField/DateTimeField.d.ts +2 -1
  35. package/dist/fields/DateTimeField/DateTimeField.d.ts.map +1 -1
  36. package/dist/fields/DateTimeField/DateTimeField.js +126 -8
  37. package/dist/fields/PasswordField/PasswordField.module.css.cjs +12 -0
  38. package/dist/fields/PasswordField/PasswordField.module.css.js +12 -0
  39. package/dist/fields/RadioField/RadioField.module.css.cjs +12 -0
  40. package/dist/fields/RadioField/RadioField.module.css.js +12 -0
  41. package/dist/fields/SliderField/SliderField.module.css.cjs +15 -3
  42. package/dist/fields/SliderField/SliderField.module.css.js +15 -3
  43. package/dist/fields/SwitchField/SwitchField.module.css.cjs +12 -0
  44. package/dist/fields/SwitchField/SwitchField.module.css.js +12 -0
  45. package/dist/fields/TimeField/TimeField.cjs +60 -5
  46. package/dist/fields/TimeField/TimeField.d.ts +2 -1
  47. package/dist/fields/TimeField/TimeField.d.ts.map +1 -1
  48. package/dist/fields/TimeField/TimeField.js +60 -5
  49. package/dist/fields/TimeField/TimePicker.cjs +182 -0
  50. package/dist/fields/TimeField/TimePicker.d.ts +11 -0
  51. package/dist/fields/TimeField/TimePicker.d.ts.map +1 -0
  52. package/dist/fields/TimeField/TimePicker.js +182 -0
  53. package/dist/fields/TimeField/TimePicker.module.css.cjs +50 -0
  54. package/dist/fields/TimeField/TimePicker.module.css.js +50 -0
  55. package/dist/fields/defaultPlaceholders.cjs +1 -0
  56. package/dist/fields/defaultPlaceholders.d.ts.map +1 -1
  57. package/dist/fields/defaultPlaceholders.js +1 -0
  58. package/dist/fields/fieldBase.module.css.cjs +366 -18
  59. package/dist/fields/fieldBase.module.css.js +366 -18
  60. package/dist/fields/icons.cjs +2 -0
  61. package/dist/fields/icons.d.ts +4 -0
  62. package/dist/fields/icons.d.ts.map +1 -1
  63. package/dist/fields/icons.js +2 -0
  64. package/dist/index.cjs +230 -3
  65. package/dist/index.d.ts +2 -0
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +230 -3
  68. package/dist/layout/Box/Box.module.css.cjs +12 -0
  69. package/dist/layout/Box/Box.module.css.js +12 -0
  70. package/dist/layout/Divider/Divider.module.css.cjs +12 -0
  71. package/dist/layout/Divider/Divider.module.css.js +12 -0
  72. package/dist/layout/Grid/Grid.module.css.cjs +12 -0
  73. package/dist/layout/Grid/Grid.module.css.js +12 -0
  74. package/dist/layout/Stack/Stack.module.css.cjs +12 -0
  75. package/dist/layout/Stack/Stack.module.css.js +12 -0
  76. package/dist/react/AxenForm.cjs +228 -3
  77. package/dist/react/AxenForm.js +228 -3
  78. package/dist/react/fieldGroup.module.css.cjs +12 -0
  79. package/dist/react/fieldGroup.module.css.js +12 -0
  80. package/dist/react/gridCell.module.css.cjs +12 -0
  81. package/dist/react/gridCell.module.css.js +12 -0
  82. package/dist/types.d.ts +1 -0
  83. package/dist/types.d.ts.map +1 -1
  84. package/package.json +4 -9
  85. package/dist/fields/AutocompleteField/AutocompleteField.module.css +0 -168
  86. package/dist/fields/CheckboxField/CheckboxField.module.css +0 -29
  87. package/dist/fields/ColorField/ColorField.module.css +0 -33
  88. package/dist/fields/PasswordField/PasswordField.module.css +0 -41
  89. package/dist/fields/RadioField/RadioField.module.css +0 -34
  90. package/dist/fields/SliderField/SliderField.module.css +0 -76
  91. package/dist/fields/SwitchField/SwitchField.module.css +0 -79
  92. package/dist/fields/fieldBase.module.css +0 -244
  93. package/dist/layout/Box/Box.module.css +0 -3
  94. package/dist/layout/Divider/Divider.module.css +0 -25
  95. package/dist/layout/Grid/Grid.module.css +0 -43
  96. package/dist/layout/Stack/Stack.module.css +0 -3
  97. package/dist/react/fieldGroup.module.css +0 -22
  98. package/dist/react/gridCell.module.css +0 -38
  99. package/dist/tokens/reset.css +0 -68
  100. package/dist/tokens/themes.css +0 -31
  101. package/dist/tokens/tokens.css +0 -120
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
4
5
  const dateUtils = require("../../core/dateUtils.cjs");
5
6
  const fieldBase_module = require("../fieldBase.module.css.cjs");
6
7
  const fieldUtils = require("../fieldUtils.cjs");
7
8
  const iconUtils = require("../iconUtils.cjs");
9
+ const DatePicker = require("./DatePicker.cjs");
8
10
  function DateField({
9
11
  name,
10
12
  value,
@@ -20,8 +22,11 @@ function DateField({
20
22
  min,
21
23
  max,
22
24
  locale,
23
- icon
25
+ icon,
26
+ form
24
27
  }) {
28
+ const [isOpen, setIsOpen] = react.useState(false);
29
+ const wrapperRef = react.useRef(null);
25
30
  const aria = fieldUtils.ariaProps(name, { error, helperText, required, label });
26
31
  const resolvedLocale = locale ?? dateUtils.getDefaultLocale();
27
32
  const resolvedIcon = iconUtils.resolveFieldIcon(icon, "date");
@@ -30,6 +35,31 @@ function DateField({
30
35
  const handleChange = (e) => {
31
36
  onChange(e);
32
37
  };
38
+ const openPicker = react.useCallback(() => {
39
+ if (!disabled && !readOnly) setIsOpen(true);
40
+ }, [disabled, readOnly]);
41
+ const handleApply = react.useCallback(
42
+ (isoDate) => {
43
+ form.setFieldValue(name, isoDate);
44
+ setIsOpen(false);
45
+ },
46
+ [form, name]
47
+ );
48
+ const handleClose = react.useCallback(() => {
49
+ setIsOpen(false);
50
+ }, []);
51
+ const handleBlur = react.useCallback(
52
+ (e) => {
53
+ setTimeout(() => {
54
+ var _a;
55
+ if (!((_a = wrapperRef.current) == null ? void 0 : _a.contains(document.activeElement))) {
56
+ setIsOpen(false);
57
+ onBlur(e);
58
+ }
59
+ }, 150);
60
+ },
61
+ [onBlur]
62
+ );
33
63
  const inputEl = /* @__PURE__ */ jsxRuntime.jsx(
34
64
  "input",
35
65
  {
@@ -38,7 +68,7 @@ function DateField({
38
68
  name,
39
69
  value: dateValue,
40
70
  onChange: handleChange,
41
- onBlur,
71
+ onBlur: handleBlur,
42
72
  placeholder,
43
73
  disabled,
44
74
  readOnly,
@@ -48,7 +78,7 @@ function DateField({
48
78
  className: fieldUtils.cx(fieldBase_module.default.input, resolvedIcon && fieldBase_module.default.inputWithIcon, error && fieldBase_module.default.inputError)
49
79
  }
50
80
  );
51
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: fieldBase_module.default.wrapper, children: [
81
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: fieldBase_module.default.wrapper, ref: wrapperRef, style: { position: "relative" }, children: [
52
82
  label && /* @__PURE__ */ jsxRuntime.jsx(
53
83
  "label",
54
84
  {
@@ -59,9 +89,31 @@ function DateField({
59
89
  }
60
90
  ),
61
91
  resolvedIcon ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: fieldBase_module.default.inputIconWrapper, children: [
62
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: fieldBase_module.default.fieldIcon, children: resolvedIcon }),
92
+ /* @__PURE__ */ jsxRuntime.jsx(
93
+ "button",
94
+ {
95
+ type: "button",
96
+ className: fieldBase_module.default.fieldIconClickable,
97
+ onClick: openPicker,
98
+ disabled: disabled || readOnly,
99
+ "aria-label": "Open date picker",
100
+ tabIndex: -1,
101
+ children: resolvedIcon
102
+ }
103
+ ),
63
104
  inputEl
64
105
  ] }) : inputEl,
106
+ isOpen && /* @__PURE__ */ jsxRuntime.jsx(
107
+ DatePicker.DatePicker,
108
+ {
109
+ value: dateValue,
110
+ min,
111
+ max,
112
+ pickerId: `${name}-datepicker`,
113
+ onApply: handleApply,
114
+ onClose: handleClose
115
+ }
116
+ ),
65
117
  displayText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: fieldBase_module.default.helperText, "aria-hidden": "true", children: displayText }),
66
118
  helperText && /* @__PURE__ */ jsxRuntime.jsx(
67
119
  "span",
@@ -1,5 +1,5 @@
1
1
  import { FieldComponentProps } from '../../types';
2
- export declare function DateField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, min, max, locale, icon, }: Readonly<FieldComponentProps & {
2
+ export declare function DateField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, min, max, locale, icon, form, }: Readonly<FieldComponentProps & {
3
3
  min?: string;
4
4
  max?: string;
5
5
  locale?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"DateField.d.ts","sourceRoot":"","sources":["../../../src/fields/DateField/DateField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAKvD,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,MAAM,EACN,KAAK,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,GAAG,EACH,GAAG,EACH,MAAM,EACN,IAAI,GACL,EAAE,QAAQ,CAAC,mBAAmB,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,2CAoEjF"}
1
+ {"version":3,"file":"DateField.d.ts","sourceRoot":"","sources":["../../../src/fields/DateField/DateField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAMvD,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,MAAM,EACN,KAAK,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,GAAG,EACH,GAAG,EACH,MAAM,EACN,IAAI,EACJ,IAAI,GACL,EAAE,QAAQ,CAAC,mBAAmB,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,2CAwHjF"}
@@ -1,8 +1,10 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useCallback } from "react";
2
3
  import { formatDateLocale, getDefaultLocale } from "../../core/dateUtils.js";
3
4
  import styles from "../fieldBase.module.css.js";
4
5
  import { ariaProps, cx, labelId, helperId } from "../fieldUtils.js";
5
6
  import { resolveFieldIcon } from "../iconUtils.js";
7
+ import { DatePicker } from "./DatePicker.js";
6
8
  function DateField({
7
9
  name,
8
10
  value,
@@ -18,8 +20,11 @@ function DateField({
18
20
  min,
19
21
  max,
20
22
  locale,
21
- icon
23
+ icon,
24
+ form
22
25
  }) {
26
+ const [isOpen, setIsOpen] = useState(false);
27
+ const wrapperRef = useRef(null);
23
28
  const aria = ariaProps(name, { error, helperText, required, label });
24
29
  const resolvedLocale = locale ?? getDefaultLocale();
25
30
  const resolvedIcon = resolveFieldIcon(icon, "date");
@@ -28,6 +33,31 @@ function DateField({
28
33
  const handleChange = (e) => {
29
34
  onChange(e);
30
35
  };
36
+ const openPicker = useCallback(() => {
37
+ if (!disabled && !readOnly) setIsOpen(true);
38
+ }, [disabled, readOnly]);
39
+ const handleApply = useCallback(
40
+ (isoDate) => {
41
+ form.setFieldValue(name, isoDate);
42
+ setIsOpen(false);
43
+ },
44
+ [form, name]
45
+ );
46
+ const handleClose = useCallback(() => {
47
+ setIsOpen(false);
48
+ }, []);
49
+ const handleBlur = useCallback(
50
+ (e) => {
51
+ setTimeout(() => {
52
+ var _a;
53
+ if (!((_a = wrapperRef.current) == null ? void 0 : _a.contains(document.activeElement))) {
54
+ setIsOpen(false);
55
+ onBlur(e);
56
+ }
57
+ }, 150);
58
+ },
59
+ [onBlur]
60
+ );
31
61
  const inputEl = /* @__PURE__ */ jsx(
32
62
  "input",
33
63
  {
@@ -36,7 +66,7 @@ function DateField({
36
66
  name,
37
67
  value: dateValue,
38
68
  onChange: handleChange,
39
- onBlur,
69
+ onBlur: handleBlur,
40
70
  placeholder,
41
71
  disabled,
42
72
  readOnly,
@@ -46,7 +76,7 @@ function DateField({
46
76
  className: cx(styles.input, resolvedIcon && styles.inputWithIcon, error && styles.inputError)
47
77
  }
48
78
  );
49
- return /* @__PURE__ */ jsxs("div", { className: styles.wrapper, children: [
79
+ return /* @__PURE__ */ jsxs("div", { className: styles.wrapper, ref: wrapperRef, style: { position: "relative" }, children: [
50
80
  label && /* @__PURE__ */ jsx(
51
81
  "label",
52
82
  {
@@ -57,9 +87,31 @@ function DateField({
57
87
  }
58
88
  ),
59
89
  resolvedIcon ? /* @__PURE__ */ jsxs("div", { className: styles.inputIconWrapper, children: [
60
- /* @__PURE__ */ jsx("span", { className: styles.fieldIcon, children: resolvedIcon }),
90
+ /* @__PURE__ */ jsx(
91
+ "button",
92
+ {
93
+ type: "button",
94
+ className: styles.fieldIconClickable,
95
+ onClick: openPicker,
96
+ disabled: disabled || readOnly,
97
+ "aria-label": "Open date picker",
98
+ tabIndex: -1,
99
+ children: resolvedIcon
100
+ }
101
+ ),
61
102
  inputEl
62
103
  ] }) : inputEl,
104
+ isOpen && /* @__PURE__ */ jsx(
105
+ DatePicker,
106
+ {
107
+ value: dateValue,
108
+ min,
109
+ max,
110
+ pickerId: `${name}-datepicker`,
111
+ onApply: handleApply,
112
+ onClose: handleClose
113
+ }
114
+ ),
63
115
  displayText && /* @__PURE__ */ jsx("span", { className: styles.helperText, "aria-hidden": "true", children: displayText }),
64
116
  helperText && /* @__PURE__ */ jsx(
65
117
  "span",
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
5
+ const DatePicker_module = require("./DatePicker.module.css.cjs");
6
+ const WEEKDAY_LABELS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
7
+ const MONTH_NAMES = [
8
+ "January",
9
+ "February",
10
+ "March",
11
+ "April",
12
+ "May",
13
+ "June",
14
+ "July",
15
+ "August",
16
+ "September",
17
+ "October",
18
+ "November",
19
+ "December"
20
+ ];
21
+ function pad2(n) {
22
+ return String(n).padStart(2, "0");
23
+ }
24
+ function toISO(year, month, day) {
25
+ return `${year}-${pad2(month + 1)}-${pad2(day)}`;
26
+ }
27
+ function parseISO(iso) {
28
+ if (!iso) return null;
29
+ const parts = iso.split("-").map(Number);
30
+ if (parts.length < 3 || parts.some((p) => !Number.isFinite(p))) return null;
31
+ return { year: parts[0], month: parts[1] - 1, day: parts[2] };
32
+ }
33
+ function daysInMonth(year, month) {
34
+ return new Date(year, month + 1, 0).getDate();
35
+ }
36
+ function startDayOfWeek(year, month) {
37
+ return new Date(year, month, 1).getDay();
38
+ }
39
+ function isSameDay(a, b) {
40
+ return a.year === b.year && a.month === b.month && a.day === b.day;
41
+ }
42
+ function buildCalendarGrid(year, month) {
43
+ const cells = [];
44
+ const firstDow = startDayOfWeek(year, month);
45
+ const totalDays = daysInMonth(year, month);
46
+ if (firstDow > 0) {
47
+ const prevMonth = month === 0 ? 11 : month - 1;
48
+ const prevYear = month === 0 ? year - 1 : year;
49
+ const prevDays = daysInMonth(prevYear, prevMonth);
50
+ for (let i = firstDow - 1; i >= 0; i--) {
51
+ cells.push({ year: prevYear, month: prevMonth, day: prevDays - i, outside: true });
52
+ }
53
+ }
54
+ for (let d = 1; d <= totalDays; d++) {
55
+ cells.push({ year, month, day: d, outside: false });
56
+ }
57
+ const nextMonth = month === 11 ? 0 : month + 1;
58
+ const nextYear = month === 11 ? year + 1 : year;
59
+ let nextDay = 1;
60
+ while (cells.length < 42) {
61
+ cells.push({ year: nextYear, month: nextMonth, day: nextDay++, outside: true });
62
+ }
63
+ return cells;
64
+ }
65
+ function DatePicker({
66
+ value,
67
+ min,
68
+ max,
69
+ pickerId,
70
+ onApply,
71
+ onClose
72
+ }) {
73
+ const parsed = parseISO(value);
74
+ const today = react.useMemo(() => {
75
+ const d = /* @__PURE__ */ new Date();
76
+ return { year: d.getFullYear(), month: d.getMonth(), day: d.getDate() };
77
+ }, []);
78
+ const [viewYear, setViewYear] = react.useState((parsed == null ? void 0 : parsed.year) ?? today.year);
79
+ const [viewMonth, setViewMonth] = react.useState((parsed == null ? void 0 : parsed.month) ?? today.month);
80
+ const minDate = react.useMemo(() => parseISO(min ?? ""), [min]);
81
+ const maxDate = react.useMemo(() => parseISO(max ?? ""), [max]);
82
+ const grid = react.useMemo(() => buildCalendarGrid(viewYear, viewMonth), [viewYear, viewMonth]);
83
+ const isDisabled = react.useCallback(
84
+ (cell) => {
85
+ const d = new Date(cell.year, cell.month, cell.day);
86
+ if (minDate) {
87
+ const minD = new Date(minDate.year, minDate.month, minDate.day);
88
+ if (d < minD) return true;
89
+ }
90
+ if (maxDate) {
91
+ const maxD = new Date(maxDate.year, maxDate.month, maxDate.day);
92
+ if (d > maxD) return true;
93
+ }
94
+ return false;
95
+ },
96
+ [minDate, maxDate]
97
+ );
98
+ const prevMonth = react.useCallback(() => {
99
+ setViewMonth((m) => {
100
+ if (m === 0) {
101
+ setViewYear((y) => y - 1);
102
+ return 11;
103
+ }
104
+ return m - 1;
105
+ });
106
+ }, []);
107
+ const nextMonth = react.useCallback(() => {
108
+ setViewMonth((m) => {
109
+ if (m === 11) {
110
+ setViewYear((y) => y + 1);
111
+ return 0;
112
+ }
113
+ return m + 1;
114
+ });
115
+ }, []);
116
+ const handleDayClick = react.useCallback(
117
+ (cell) => {
118
+ if (isDisabled(cell)) return;
119
+ onApply(toISO(cell.year, cell.month, cell.day));
120
+ },
121
+ [isDisabled, onApply]
122
+ );
123
+ const handleToday = react.useCallback(() => {
124
+ onApply(toISO(today.year, today.month, today.day));
125
+ }, [today, onApply]);
126
+ const handleClear = react.useCallback(() => {
127
+ onApply("");
128
+ }, [onApply]);
129
+ react.useEffect(() => {
130
+ const handleKeyDown = (e) => {
131
+ if (e.key === "Escape") onClose();
132
+ };
133
+ document.addEventListener("keydown", handleKeyDown);
134
+ return () => document.removeEventListener("keydown", handleKeyDown);
135
+ }, [onClose]);
136
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
137
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: DatePicker_module.default.overlay, onClick: onClose, "aria-hidden": "true" }),
138
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: DatePicker_module.default.picker, id: pickerId, "aria-label": "Date picker", children: [
139
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: DatePicker_module.default.header, children: [
140
+ /* @__PURE__ */ jsxRuntime.jsx(
141
+ "button",
142
+ {
143
+ type: "button",
144
+ className: DatePicker_module.default.navBtn,
145
+ onClick: prevMonth,
146
+ "aria-label": "Previous month",
147
+ children: "‹"
148
+ }
149
+ ),
150
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: DatePicker_module.default.headerTitle, children: [
151
+ MONTH_NAMES[viewMonth],
152
+ " ",
153
+ viewYear
154
+ ] }),
155
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: DatePicker_module.default.navBtn, onClick: nextMonth, "aria-label": "Next month", children: "›" })
156
+ ] }),
157
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: DatePicker_module.default.weekdays, children: WEEKDAY_LABELS.map((wd) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: DatePicker_module.default.weekday, children: wd }, wd)) }),
158
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: DatePicker_module.default.grid, role: "radiogroup", "aria-label": "Calendar days", children: grid.map((cell) => {
159
+ const iso = toISO(cell.year, cell.month, cell.day);
160
+ const isSelected = parsed != null && isSameDay(cell, parsed);
161
+ const isToday = isSameDay(cell, today);
162
+ const dow = new Date(cell.year, cell.month, cell.day).getDay();
163
+ const isWeekend = dow === 0 || dow === 6;
164
+ const disabled = isDisabled(cell);
165
+ const cls = [
166
+ DatePicker_module.default.day,
167
+ cell.outside ? DatePicker_module.default.dayOutside : "",
168
+ isToday ? DatePicker_module.default.dayToday : "",
169
+ isSelected ? DatePicker_module.default.daySelected : "",
170
+ isWeekend && !isSelected ? DatePicker_module.default.dayWeekend : ""
171
+ ].filter(Boolean).join(" ");
172
+ return /* @__PURE__ */ jsxRuntime.jsx(
173
+ "button",
174
+ {
175
+ type: "button",
176
+ role: "radio",
177
+ "aria-checked": isSelected,
178
+ "aria-label": iso,
179
+ className: cls,
180
+ disabled,
181
+ onClick: () => handleDayClick(cell),
182
+ children: cell.day
183
+ },
184
+ iso + (cell.outside ? "-out" : "")
185
+ );
186
+ }) }),
187
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: DatePicker_module.default.footer, children: [
188
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: DatePicker_module.default.footerBtn, onClick: handleClear, children: "Clear" }),
189
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: DatePicker_module.default.footerBtn, onClick: handleToday, children: "Today" })
190
+ ] })
191
+ ] })
192
+ ] });
193
+ }
194
+ exports.DatePicker = DatePicker;
@@ -0,0 +1,11 @@
1
+ interface DatePickerProps {
2
+ value: string;
3
+ min?: string;
4
+ max?: string;
5
+ pickerId: string;
6
+ onApply: (isoDate: string) => void;
7
+ onClose: () => void;
8
+ }
9
+ export declare function DatePicker({ value, min, max, pickerId, onApply, onClose, }: Readonly<DatePickerProps>): import("react/jsx-runtime").JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=DatePicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/fields/DateField/DatePicker.tsx"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAsFD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,GAAG,EACH,GAAG,EACH,QAAQ,EACR,OAAO,EACP,OAAO,GACR,EAAE,QAAQ,CAAC,eAAe,CAAC,2CA4J3B"}
@@ -0,0 +1,194 @@
1
+ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
+ import { useMemo, useState, useCallback, useEffect } from "react";
3
+ import s from "./DatePicker.module.css.js";
4
+ const WEEKDAY_LABELS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
5
+ const MONTH_NAMES = [
6
+ "January",
7
+ "February",
8
+ "March",
9
+ "April",
10
+ "May",
11
+ "June",
12
+ "July",
13
+ "August",
14
+ "September",
15
+ "October",
16
+ "November",
17
+ "December"
18
+ ];
19
+ function pad2(n) {
20
+ return String(n).padStart(2, "0");
21
+ }
22
+ function toISO(year, month, day) {
23
+ return `${year}-${pad2(month + 1)}-${pad2(day)}`;
24
+ }
25
+ function parseISO(iso) {
26
+ if (!iso) return null;
27
+ const parts = iso.split("-").map(Number);
28
+ if (parts.length < 3 || parts.some((p) => !Number.isFinite(p))) return null;
29
+ return { year: parts[0], month: parts[1] - 1, day: parts[2] };
30
+ }
31
+ function daysInMonth(year, month) {
32
+ return new Date(year, month + 1, 0).getDate();
33
+ }
34
+ function startDayOfWeek(year, month) {
35
+ return new Date(year, month, 1).getDay();
36
+ }
37
+ function isSameDay(a, b) {
38
+ return a.year === b.year && a.month === b.month && a.day === b.day;
39
+ }
40
+ function buildCalendarGrid(year, month) {
41
+ const cells = [];
42
+ const firstDow = startDayOfWeek(year, month);
43
+ const totalDays = daysInMonth(year, month);
44
+ if (firstDow > 0) {
45
+ const prevMonth = month === 0 ? 11 : month - 1;
46
+ const prevYear = month === 0 ? year - 1 : year;
47
+ const prevDays = daysInMonth(prevYear, prevMonth);
48
+ for (let i = firstDow - 1; i >= 0; i--) {
49
+ cells.push({ year: prevYear, month: prevMonth, day: prevDays - i, outside: true });
50
+ }
51
+ }
52
+ for (let d = 1; d <= totalDays; d++) {
53
+ cells.push({ year, month, day: d, outside: false });
54
+ }
55
+ const nextMonth = month === 11 ? 0 : month + 1;
56
+ const nextYear = month === 11 ? year + 1 : year;
57
+ let nextDay = 1;
58
+ while (cells.length < 42) {
59
+ cells.push({ year: nextYear, month: nextMonth, day: nextDay++, outside: true });
60
+ }
61
+ return cells;
62
+ }
63
+ function DatePicker({
64
+ value,
65
+ min,
66
+ max,
67
+ pickerId,
68
+ onApply,
69
+ onClose
70
+ }) {
71
+ const parsed = parseISO(value);
72
+ const today = useMemo(() => {
73
+ const d = /* @__PURE__ */ new Date();
74
+ return { year: d.getFullYear(), month: d.getMonth(), day: d.getDate() };
75
+ }, []);
76
+ const [viewYear, setViewYear] = useState((parsed == null ? void 0 : parsed.year) ?? today.year);
77
+ const [viewMonth, setViewMonth] = useState((parsed == null ? void 0 : parsed.month) ?? today.month);
78
+ const minDate = useMemo(() => parseISO(min ?? ""), [min]);
79
+ const maxDate = useMemo(() => parseISO(max ?? ""), [max]);
80
+ const grid = useMemo(() => buildCalendarGrid(viewYear, viewMonth), [viewYear, viewMonth]);
81
+ const isDisabled = useCallback(
82
+ (cell) => {
83
+ const d = new Date(cell.year, cell.month, cell.day);
84
+ if (minDate) {
85
+ const minD = new Date(minDate.year, minDate.month, minDate.day);
86
+ if (d < minD) return true;
87
+ }
88
+ if (maxDate) {
89
+ const maxD = new Date(maxDate.year, maxDate.month, maxDate.day);
90
+ if (d > maxD) return true;
91
+ }
92
+ return false;
93
+ },
94
+ [minDate, maxDate]
95
+ );
96
+ const prevMonth = useCallback(() => {
97
+ setViewMonth((m) => {
98
+ if (m === 0) {
99
+ setViewYear((y) => y - 1);
100
+ return 11;
101
+ }
102
+ return m - 1;
103
+ });
104
+ }, []);
105
+ const nextMonth = useCallback(() => {
106
+ setViewMonth((m) => {
107
+ if (m === 11) {
108
+ setViewYear((y) => y + 1);
109
+ return 0;
110
+ }
111
+ return m + 1;
112
+ });
113
+ }, []);
114
+ const handleDayClick = useCallback(
115
+ (cell) => {
116
+ if (isDisabled(cell)) return;
117
+ onApply(toISO(cell.year, cell.month, cell.day));
118
+ },
119
+ [isDisabled, onApply]
120
+ );
121
+ const handleToday = useCallback(() => {
122
+ onApply(toISO(today.year, today.month, today.day));
123
+ }, [today, onApply]);
124
+ const handleClear = useCallback(() => {
125
+ onApply("");
126
+ }, [onApply]);
127
+ useEffect(() => {
128
+ const handleKeyDown = (e) => {
129
+ if (e.key === "Escape") onClose();
130
+ };
131
+ document.addEventListener("keydown", handleKeyDown);
132
+ return () => document.removeEventListener("keydown", handleKeyDown);
133
+ }, [onClose]);
134
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
135
+ /* @__PURE__ */ jsx("div", { className: s.overlay, onClick: onClose, "aria-hidden": "true" }),
136
+ /* @__PURE__ */ jsxs("div", { className: s.picker, id: pickerId, "aria-label": "Date picker", children: [
137
+ /* @__PURE__ */ jsxs("div", { className: s.header, children: [
138
+ /* @__PURE__ */ jsx(
139
+ "button",
140
+ {
141
+ type: "button",
142
+ className: s.navBtn,
143
+ onClick: prevMonth,
144
+ "aria-label": "Previous month",
145
+ children: "‹"
146
+ }
147
+ ),
148
+ /* @__PURE__ */ jsxs("span", { className: s.headerTitle, children: [
149
+ MONTH_NAMES[viewMonth],
150
+ " ",
151
+ viewYear
152
+ ] }),
153
+ /* @__PURE__ */ jsx("button", { type: "button", className: s.navBtn, onClick: nextMonth, "aria-label": "Next month", children: "›" })
154
+ ] }),
155
+ /* @__PURE__ */ jsx("div", { className: s.weekdays, children: WEEKDAY_LABELS.map((wd) => /* @__PURE__ */ jsx("div", { className: s.weekday, children: wd }, wd)) }),
156
+ /* @__PURE__ */ jsx("div", { className: s.grid, role: "radiogroup", "aria-label": "Calendar days", children: grid.map((cell) => {
157
+ const iso = toISO(cell.year, cell.month, cell.day);
158
+ const isSelected = parsed != null && isSameDay(cell, parsed);
159
+ const isToday = isSameDay(cell, today);
160
+ const dow = new Date(cell.year, cell.month, cell.day).getDay();
161
+ const isWeekend = dow === 0 || dow === 6;
162
+ const disabled = isDisabled(cell);
163
+ const cls = [
164
+ s.day,
165
+ cell.outside ? s.dayOutside : "",
166
+ isToday ? s.dayToday : "",
167
+ isSelected ? s.daySelected : "",
168
+ isWeekend && !isSelected ? s.dayWeekend : ""
169
+ ].filter(Boolean).join(" ");
170
+ return /* @__PURE__ */ jsx(
171
+ "button",
172
+ {
173
+ type: "button",
174
+ role: "radio",
175
+ "aria-checked": isSelected,
176
+ "aria-label": iso,
177
+ className: cls,
178
+ disabled,
179
+ onClick: () => handleDayClick(cell),
180
+ children: cell.day
181
+ },
182
+ iso + (cell.outside ? "-out" : "")
183
+ );
184
+ }) }),
185
+ /* @__PURE__ */ jsxs("div", { className: s.footer, children: [
186
+ /* @__PURE__ */ jsx("button", { type: "button", className: s.footerBtn, onClick: handleClear, children: "Clear" }),
187
+ /* @__PURE__ */ jsx("button", { type: "button", className: s.footerBtn, onClick: handleToday, children: "Today" })
188
+ ] })
189
+ ] })
190
+ ] });
191
+ }
192
+ export {
193
+ DatePicker
194
+ };