@orianatech/pire 0.21.0 → 0.22.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.
@@ -1,10 +1,23 @@
1
1
  import type * as React from 'react';
2
2
  import type { DateValue } from 'react-aria-components';
3
- export interface DatePickerProps {
3
+ export interface DatePickerProps<TFormat extends 'object' | 'iso' = 'object'> {
4
4
  label?: React.ReactNode;
5
- value?: DateValue | null;
6
- defaultValue?: DateValue | null;
7
- onChange?: (value: DateValue | null) => void;
5
+ /** A `DateValue`, or the `YYYY-MM-DD` string one arrived as — a value straight off a JSON
6
+ * response needs no conversion. A datetime is accepted and trimmed to its date (`date`
7
+ * columns commonly serialise as `"2026-08-14T00:00:00.000000Z"`); the time and zone are
8
+ * discarded, because a `DateValue` is a calendar date and holds neither. A malformed date
9
+ * shows as an empty field rather than throwing. */
10
+ value?: DateValue | string | null;
11
+ defaultValue?: DateValue | string | null;
12
+ onChange?: (value: (TFormat extends 'iso' ? string : DateValue) | null) => void;
13
+ /**
14
+ * The shape `onChange` emits.
15
+ *
16
+ * `'object'` (the default) emits the `@internationalized/date` `DateValue` this component has
17
+ * always emitted. `'iso'` emits the `YYYY-MM-DD` string a consumer sends back to its API, and
18
+ * `null` for an empty field. Input accepts both shapes in either mode.
19
+ */
20
+ valueFormat?: TFormat;
8
21
  minValue?: DateValue;
9
22
  maxValue?: DateValue;
10
23
  /** Return true for dates that cannot be picked — factory calendar, blocked periods. */
@@ -24,4 +37,4 @@ export interface DatePickerProps {
24
37
  /** Date entry with a segmented field and a calendar popover. Segments follow the user's locale,
25
38
  * so a German user types dd.mm.yyyy without any prop change. Wrap the app in React Aria's
26
39
  * I18nProvider to pin a locale. */
27
- export declare function DatePicker({ label, description, errorMessage, size, fullWidth, className, style, ...rest }: DatePickerProps): React.JSX.Element;
40
+ export declare function DatePicker<TFormat extends 'object' | 'iso' = 'object'>({ label, description, errorMessage, size, fullWidth, className, style, value, defaultValue, onChange, valueFormat, ...rest }: DatePickerProps<TFormat>): React.JSX.Element;
@@ -1,10 +1,23 @@
1
1
  import type * as React from 'react';
2
2
  import type { DateValue } from 'react-aria-components';
3
- export interface DatePickerProps {
3
+ export interface DatePickerProps<TFormat extends 'object' | 'iso' = 'object'> {
4
4
  label?: React.ReactNode;
5
- value?: DateValue | null;
6
- defaultValue?: DateValue | null;
7
- onChange?: (value: DateValue | null) => void;
5
+ /** A `DateValue`, or the `YYYY-MM-DD` string one arrived as — a value straight off a JSON
6
+ * response needs no conversion. A datetime is accepted and trimmed to its date (`date`
7
+ * columns commonly serialise as `"2026-08-14T00:00:00.000000Z"`); the time and zone are
8
+ * discarded, because a `DateValue` is a calendar date and holds neither. A malformed date
9
+ * shows as an empty field rather than throwing. */
10
+ value?: DateValue | string | null;
11
+ defaultValue?: DateValue | string | null;
12
+ onChange?: (value: (TFormat extends 'iso' ? string : DateValue) | null) => void;
13
+ /**
14
+ * The shape `onChange` emits.
15
+ *
16
+ * `'object'` (the default) emits the `@internationalized/date` `DateValue` this component has
17
+ * always emitted. `'iso'` emits the `YYYY-MM-DD` string a consumer sends back to its API, and
18
+ * `null` for an empty field. Input accepts both shapes in either mode.
19
+ */
20
+ valueFormat?: TFormat;
8
21
  minValue?: DateValue;
9
22
  maxValue?: DateValue;
10
23
  /** Return true for dates that cannot be picked — factory calendar, blocked periods. */
@@ -24,5 +37,5 @@ export interface DatePickerProps {
24
37
  /** Date entry with a segmented field and a calendar popover. Segments follow the user's locale,
25
38
  * so a German user types dd.mm.yyyy without any prop change. Wrap the app in React Aria's
26
39
  * I18nProvider to pin a locale. */
27
- export declare function DatePicker({ label, description, errorMessage, size, fullWidth, className, style, ...rest }: DatePickerProps): React.JSX.Element;
40
+ export declare function DatePicker<TFormat extends 'object' | 'iso' = 'object'>({ label, description, errorMessage, size, fullWidth, className, style, value, defaultValue, onChange, valueFormat, ...rest }: DatePickerProps<TFormat>): React.JSX.Element;
28
41
  //# sourceMappingURL=DatePicker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DatePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAIpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAKvD,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,YAAY,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC;IACjD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED;;oCAEoC;AACpC,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,IAAW,EAAE,SAAgB,EAC1F,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,eAAe,qBA6C7C"}
1
+ {"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DatePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAIpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAMvD,MAAM,WAAW,eAAe,CAAC,OAAO,SAAS,QAAQ,GAAG,KAAK,GAAG,QAAQ;IAC1E,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB;;;;wDAIoD;IACpD,KAAK,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,SAAS,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAChF;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC;IACjD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAwBD;;oCAEoC;AACpC,wBAAgB,UAAU,CAAC,OAAO,SAAS,QAAQ,GAAG,KAAK,GAAG,QAAQ,EAAE,EAAE,KAAK,EAAE,WAAW,EAC1F,YAAY,EAAE,IAAW,EAAE,SAAgB,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAC5F,WAAW,EAAE,GAAG,IAAI,EAAE,EAAE,eAAe,CAAC,OAAO,CAAC,qBAoDjD"}
@@ -1,12 +1,27 @@
1
1
  import type * as React from 'react';
2
- export interface NumberFieldProps {
2
+ export interface NumberFieldProps<TEmpty extends '' | null = never> {
3
3
  label?: React.ReactNode;
4
4
  /** Accessible name when `label` is omitted — e.g. in a table cell where the column header
5
5
  * already carries the visible text. A field with neither is unnamed and fails axe. */
6
6
  'aria-label'?: string;
7
- value?: number;
8
- defaultValue?: number;
9
- onChange?: (value: number) => void;
7
+ /** A figure, or the string one arrived as — a `decimal` column serialises as `"1500.00"`, so
8
+ * a value straight off a JSON response needs no conversion. Strings are read with `Number`,
9
+ * which is machine format only: `"1500.00"`, never the `"1.500,00"` the field *displays*
10
+ * under es-PY. Anything unparseable shows as empty rather than as `NaN`. */
11
+ value?: number | string | null;
12
+ defaultValue?: number | string | null;
13
+ onChange?: (value: number | NoInfer<TEmpty>) => void;
14
+ /**
15
+ * What this field uses to mean "empty", in both directions: accepted in `value` and emitted by
16
+ * `onChange`.
17
+ *
18
+ * Defaults to `NaN`, which is what React Aria uses internally and what this component has
19
+ * always emitted. Declare `''` or `null` when the form is serialised to JSON: `NaN` does not
20
+ * survive `JSON.stringify` (it becomes `null`) and compares `false` against everything
21
+ * including itself, so a guard like `Math.abs(total) > 0` reads an empty field as a balanced
22
+ * one.
23
+ */
24
+ emptyValue?: TEmpty;
10
25
  minValue?: number;
11
26
  maxValue?: number;
12
27
  step?: number;
@@ -45,4 +60,4 @@ export interface NumberFieldProps {
45
60
  * Enable the action and validate when it is pressed, surfacing the reason. A disabled control that
46
61
  * does not say why is a worse affordance regardless, so the fix costs nothing.
47
62
  */
48
- export declare function NumberField({ label, suffix, description, errorMessage, hideStepper, size, fullWidth, className, style, ...rest }: NumberFieldProps): React.JSX.Element;
63
+ export declare function NumberField<TEmpty extends '' | null = never>({ label, suffix, description, errorMessage, hideStepper, size, fullWidth, className, style, value, defaultValue, onChange, emptyValue, ...rest }: NumberFieldProps<TEmpty>): React.JSX.Element;
@@ -1,12 +1,27 @@
1
1
  import type * as React from 'react';
2
- export interface NumberFieldProps {
2
+ export interface NumberFieldProps<TEmpty extends '' | null = never> {
3
3
  label?: React.ReactNode;
4
4
  /** Accessible name when `label` is omitted — e.g. in a table cell where the column header
5
5
  * already carries the visible text. A field with neither is unnamed and fails axe. */
6
6
  'aria-label'?: string;
7
- value?: number;
8
- defaultValue?: number;
9
- onChange?: (value: number) => void;
7
+ /** A figure, or the string one arrived as — a `decimal` column serialises as `"1500.00"`, so
8
+ * a value straight off a JSON response needs no conversion. Strings are read with `Number`,
9
+ * which is machine format only: `"1500.00"`, never the `"1.500,00"` the field *displays*
10
+ * under es-PY. Anything unparseable shows as empty rather than as `NaN`. */
11
+ value?: number | string | null;
12
+ defaultValue?: number | string | null;
13
+ onChange?: (value: number | NoInfer<TEmpty>) => void;
14
+ /**
15
+ * What this field uses to mean "empty", in both directions: accepted in `value` and emitted by
16
+ * `onChange`.
17
+ *
18
+ * Defaults to `NaN`, which is what React Aria uses internally and what this component has
19
+ * always emitted. Declare `''` or `null` when the form is serialised to JSON: `NaN` does not
20
+ * survive `JSON.stringify` (it becomes `null`) and compares `false` against everything
21
+ * including itself, so a guard like `Math.abs(total) > 0` reads an empty field as a balanced
22
+ * one.
23
+ */
24
+ emptyValue?: TEmpty;
10
25
  minValue?: number;
11
26
  maxValue?: number;
12
27
  step?: number;
@@ -45,5 +60,5 @@ export interface NumberFieldProps {
45
60
  * Enable the action and validate when it is pressed, surfacing the reason. A disabled control that
46
61
  * does not say why is a worse affordance regardless, so the fix costs nothing.
47
62
  */
48
- export declare function NumberField({ label, suffix, description, errorMessage, hideStepper, size, fullWidth, className, style, ...rest }: NumberFieldProps): React.JSX.Element;
63
+ export declare function NumberField<TEmpty extends '' | null = never>({ label, suffix, description, errorMessage, hideStepper, size, fullWidth, className, style, value, defaultValue, onChange, emptyValue, ...rest }: NumberFieldProps<TEmpty>): React.JSX.Element;
49
64
  //# sourceMappingURL=NumberField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NumberField.d.ts","sourceRoot":"","sources":["../../../src/components/forms/NumberField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAkCpC,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB;2FACuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sGAAsG;IACtG,aAAa,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC;IACzC,uDAAuD;IACvD,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,IAAW,EAC9F,SAAgB,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,gBAAgB,qBA2DhE"}
1
+ {"version":3,"file":"NumberField.d.ts","sourceRoot":"","sources":["../../../src/components/forms/NumberField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAkCpC,MAAM,WAAW,gBAAgB,CAAC,MAAM,SAAS,EAAE,GAAG,IAAI,GAAG,KAAK;IAChE,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB;2FACuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;iFAG6E;IAC7E,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAMtC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACrD;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sGAAsG;IACtG,aAAa,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC;IACzC,uDAAuD;IACvD,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAeD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,MAAM,SAAS,EAAE,GAAG,IAAI,GAAG,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EACxF,YAAY,EAAE,WAAW,EAAE,IAAW,EAAE,SAAgB,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAC/F,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,EAAE,gBAAgB,CAAC,MAAM,CAAC,qBA+D1D"}
package/dist/index.cjs CHANGED
@@ -1515,6 +1515,15 @@ function typeIntoInput(el, text) {
1515
1515
  const caret = start + text.length;
1516
1516
  el.setSelectionRange(caret, caret);
1517
1517
  }
1518
+ function toAriaNumber(value) {
1519
+ if (value === void 0) {
1520
+ return void 0;
1521
+ }
1522
+ if (value === null || value === "") {
1523
+ return NaN;
1524
+ }
1525
+ return typeof value === "number" ? value : Number(value);
1526
+ }
1518
1527
  function NumberField({
1519
1528
  label,
1520
1529
  suffix,
@@ -1525,10 +1534,16 @@ function NumberField({
1525
1534
  fullWidth = true,
1526
1535
  className,
1527
1536
  style,
1537
+ value,
1538
+ defaultValue,
1539
+ onChange,
1540
+ emptyValue,
1528
1541
  ...rest
1529
1542
  }) {
1530
1543
  const msg = usePireMessages();
1531
1544
  const { locale } = (0, import_react_aria_components17.useLocale)();
1545
+ const empty = emptyValue === void 0 ? NaN : emptyValue;
1546
+ const handleChange = onChange && ((next) => onChange(Number.isNaN(next) ? empty : next));
1532
1547
  const handleDecimalKey = (event) => {
1533
1548
  if (event.key !== "." || event.ctrlKey || event.metaKey || event.altKey) {
1534
1549
  return;
@@ -1547,37 +1562,71 @@ function NumberField({
1547
1562
  event.preventDefault();
1548
1563
  typeIntoInput(event.currentTarget, separator);
1549
1564
  };
1550
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_aria_components17.NumberField, { className: cx("pire-field", className), style, validationBehavior: "aria", ...rest, children: [
1551
- label ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_aria_components17.Label, { className: "pire-field-label", children: [
1552
- label,
1553
- rest.isRequired ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
1554
- ] }) : null,
1555
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1556
- import_react_aria_components17.Group,
1557
- {
1558
- className: "pire-control",
1559
- "data-size": size,
1560
- "data-full-width": String(fullWidth),
1561
- "data-invalid": rest.isInvalid ? "true" : void 0,
1562
- "data-readonly": rest.isReadOnly ? "true" : void 0,
1563
- children: [
1564
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Input, { className: "pire-input", "data-numeric": "true", onKeyDown: handleDecimalKey }),
1565
- suffix ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "pire-affix", children: suffix }) : null,
1566
- hideStepper ? null : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "pire-stepper", children: [
1567
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Button, { slot: "increment", className: "pire-stepper-btn", "aria-label": msg.numberFieldIncrease, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Icon, { name: "plus", size: 10 }) }),
1568
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Button, { slot: "decrement", className: "pire-stepper-btn", "aria-label": msg.numberFieldDecrease, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Icon, { name: "minus", size: 10 }) })
1569
- ] })
1570
- ]
1571
- }
1572
- ),
1573
- description ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Text, { slot: "description", className: "pire-field-hint", children: description }) : null,
1574
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.FieldError, { className: "pire-field-error", children: errorMessage })
1575
- ] });
1565
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1566
+ import_react_aria_components17.NumberField,
1567
+ {
1568
+ className: cx("pire-field", className),
1569
+ style,
1570
+ validationBehavior: "aria",
1571
+ value: toAriaNumber(value),
1572
+ defaultValue: toAriaNumber(defaultValue),
1573
+ onChange: handleChange,
1574
+ ...rest,
1575
+ children: [
1576
+ label ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_aria_components17.Label, { className: "pire-field-label", children: [
1577
+ label,
1578
+ rest.isRequired ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
1579
+ ] }) : null,
1580
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1581
+ import_react_aria_components17.Group,
1582
+ {
1583
+ className: "pire-control",
1584
+ "data-size": size,
1585
+ "data-full-width": String(fullWidth),
1586
+ "data-invalid": rest.isInvalid ? "true" : void 0,
1587
+ "data-readonly": rest.isReadOnly ? "true" : void 0,
1588
+ children: [
1589
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Input, { className: "pire-input", "data-numeric": "true", onKeyDown: handleDecimalKey }),
1590
+ suffix ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "pire-affix", children: suffix }) : null,
1591
+ hideStepper ? null : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "pire-stepper", children: [
1592
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Button, { slot: "increment", className: "pire-stepper-btn", "aria-label": msg.numberFieldIncrease, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Icon, { name: "plus", size: 10 }) }),
1593
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Button, { slot: "decrement", className: "pire-stepper-btn", "aria-label": msg.numberFieldDecrease, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Icon, { name: "minus", size: 10 }) })
1594
+ ] })
1595
+ ]
1596
+ }
1597
+ ),
1598
+ description ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Text, { slot: "description", className: "pire-field-hint", children: description }) : null,
1599
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.FieldError, { className: "pire-field-error", children: errorMessage })
1600
+ ]
1601
+ }
1602
+ );
1576
1603
  }
1577
1604
 
1578
1605
  // src/components/forms/DatePicker.tsx
1579
1606
  var import_react_aria_components18 = require("react-aria-components");
1607
+ var import_date = require("@internationalized/date");
1580
1608
  var import_jsx_runtime26 = require("react/jsx-runtime");
1609
+ var ISO_DATE = /^(\d{4}-\d{2}-\d{2})(?:[T ].*)?$/;
1610
+ function coerceDateValue(value) {
1611
+ if (value === void 0) {
1612
+ return void 0;
1613
+ }
1614
+ if (value === null || value === "") {
1615
+ return null;
1616
+ }
1617
+ if (typeof value !== "string") {
1618
+ return value;
1619
+ }
1620
+ const match = ISO_DATE.exec(value);
1621
+ if (!match) {
1622
+ return null;
1623
+ }
1624
+ try {
1625
+ return (0, import_date.parseDate)(match[1]);
1626
+ } catch {
1627
+ return null;
1628
+ }
1629
+ }
1581
1630
  function DatePicker({
1582
1631
  label,
1583
1632
  description,
@@ -1586,42 +1635,61 @@ function DatePicker({
1586
1635
  fullWidth = true,
1587
1636
  className,
1588
1637
  style,
1638
+ value,
1639
+ defaultValue,
1640
+ onChange,
1641
+ valueFormat,
1589
1642
  ...rest
1590
1643
  }) {
1591
1644
  const msg = usePireMessages();
1592
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_aria_components18.DatePicker, { className: cx("pire-field", className), style, validationBehavior: "aria", ...rest, children: [
1593
- label ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_aria_components18.Label, { className: "pire-field-label", children: [
1594
- label,
1595
- rest.isRequired ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
1596
- ] }) : null,
1597
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1598
- import_react_aria_components18.Group,
1599
- {
1600
- className: "pire-control",
1601
- "data-size": size,
1602
- "data-full-width": String(fullWidth),
1603
- "data-invalid": rest.isInvalid ? "true" : void 0,
1604
- "data-readonly": rest.isReadOnly ? "true" : void 0,
1605
- children: [
1606
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.DateInput, { className: "pire-datefield", children: (segment) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.DateSegment, { segment, className: "pire-datesegment" }) }),
1607
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Button, { className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerOpenCalendar, style: { width: 24, height: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "calendar", size: 16 }) })
1608
- ]
1609
- }
1610
- ),
1611
- description ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Text, { slot: "description", className: "pire-field-hint", children: description }) : null,
1612
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.FieldError, { className: "pire-field-error", children: errorMessage }),
1613
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Popover, { className: "pire-popover", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Dialog, { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_aria_components18.Calendar, { className: "pire-calendar", children: [
1614
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("header", { className: "pire-calendar-head", children: [
1615
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Button, { slot: "previous", className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerPreviousMonth, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "chevron-left", size: 16 }) }),
1616
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Heading, { className: "pire-calendar-title" }),
1617
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Button, { slot: "next", className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerNextMonth, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "chevron-right", size: 16 }) })
1618
- ] }),
1619
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_aria_components18.CalendarGrid, { className: "pire-calendar-grid", children: [
1620
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.CalendarGridHeader, { children: (day) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.CalendarHeaderCell, { children: day }) }),
1621
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.CalendarGridBody, { children: (date) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.CalendarCell, { date, className: "pire-calendar-cell" }) })
1622
- ] })
1623
- ] }) }) })
1624
- ] });
1645
+ const handleChange = onChange && ((next) => onChange(
1646
+ valueFormat === "iso" && next ? next.toString() : next
1647
+ ));
1648
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1649
+ import_react_aria_components18.DatePicker,
1650
+ {
1651
+ className: cx("pire-field", className),
1652
+ style,
1653
+ validationBehavior: "aria",
1654
+ value: coerceDateValue(value),
1655
+ defaultValue: coerceDateValue(defaultValue),
1656
+ onChange: handleChange,
1657
+ ...rest,
1658
+ children: [
1659
+ label ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_aria_components18.Label, { className: "pire-field-label", children: [
1660
+ label,
1661
+ rest.isRequired ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
1662
+ ] }) : null,
1663
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1664
+ import_react_aria_components18.Group,
1665
+ {
1666
+ className: "pire-control",
1667
+ "data-size": size,
1668
+ "data-full-width": String(fullWidth),
1669
+ "data-invalid": rest.isInvalid ? "true" : void 0,
1670
+ "data-readonly": rest.isReadOnly ? "true" : void 0,
1671
+ children: [
1672
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.DateInput, { className: "pire-datefield", children: (segment) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.DateSegment, { segment, className: "pire-datesegment" }) }),
1673
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Button, { className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerOpenCalendar, style: { width: 24, height: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "calendar", size: 16 }) })
1674
+ ]
1675
+ }
1676
+ ),
1677
+ description ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Text, { slot: "description", className: "pire-field-hint", children: description }) : null,
1678
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.FieldError, { className: "pire-field-error", children: errorMessage }),
1679
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Popover, { className: "pire-popover", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Dialog, { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_aria_components18.Calendar, { className: "pire-calendar", children: [
1680
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("header", { className: "pire-calendar-head", children: [
1681
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Button, { slot: "previous", className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerPreviousMonth, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "chevron-left", size: 16 }) }),
1682
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Heading, { className: "pire-calendar-title" }),
1683
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Button, { slot: "next", className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerNextMonth, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "chevron-right", size: 16 }) })
1684
+ ] }),
1685
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_aria_components18.CalendarGrid, { className: "pire-calendar-grid", children: [
1686
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.CalendarGridHeader, { children: (day) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.CalendarHeaderCell, { children: day }) }),
1687
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.CalendarGridBody, { children: (date) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.CalendarCell, { date, className: "pire-calendar-cell" }) })
1688
+ ] })
1689
+ ] }) }) })
1690
+ ]
1691
+ }
1692
+ );
1625
1693
  }
1626
1694
 
1627
1695
  // src/components/forms/DateRangePicker.tsx