@orianatech/pire 0.20.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.
Files changed (35) hide show
  1. package/dist/components/core/Badge.d.cts +14 -3
  2. package/dist/components/core/Badge.d.ts +14 -3
  3. package/dist/components/core/Badge.d.ts.map +1 -1
  4. package/dist/components/core/Tag.d.cts +8 -3
  5. package/dist/components/core/Tag.d.ts +8 -3
  6. package/dist/components/core/Tag.d.ts.map +1 -1
  7. package/dist/components/core/Text.d.cts +9 -4
  8. package/dist/components/core/Text.d.ts +9 -4
  9. package/dist/components/core/Text.d.ts.map +1 -1
  10. package/dist/components/data/DataTable.d.cts +9 -1
  11. package/dist/components/data/DataTable.d.ts +9 -1
  12. package/dist/components/data/DataTable.d.ts.map +1 -1
  13. package/dist/components/forms/ComboBox.d.cts +12 -2
  14. package/dist/components/forms/ComboBox.d.ts +12 -2
  15. package/dist/components/forms/ComboBox.d.ts.map +1 -1
  16. package/dist/components/forms/DatePicker.d.cts +18 -5
  17. package/dist/components/forms/DatePicker.d.ts +18 -5
  18. package/dist/components/forms/DatePicker.d.ts.map +1 -1
  19. package/dist/components/forms/FileUpload.d.cts +10 -2
  20. package/dist/components/forms/FileUpload.d.ts +10 -2
  21. package/dist/components/forms/FileUpload.d.ts.map +1 -1
  22. package/dist/components/forms/NumberField.d.cts +37 -7
  23. package/dist/components/forms/NumberField.d.ts +37 -7
  24. package/dist/components/forms/NumberField.d.ts.map +1 -1
  25. package/dist/components/patterns/FileDropZone.d.cts +8 -1
  26. package/dist/components/patterns/FileDropZone.d.ts +8 -1
  27. package/dist/components/patterns/FileDropZone.d.ts.map +1 -1
  28. package/dist/i18n/messages.d.cts +2 -0
  29. package/dist/i18n/messages.d.ts +2 -0
  30. package/dist/i18n/messages.d.ts.map +1 -1
  31. package/dist/index.cjs +285 -150
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.js +305 -169
  34. package/dist/index.js.map +1 -1
  35. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -461,6 +461,7 @@ var IconButton = React3.forwardRef(function IconButton2({
461
461
  });
462
462
 
463
463
  // src/components/core/Badge.tsx
464
+ import * as React4 from "react";
464
465
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
465
466
  var STATUS_ICON = {
466
467
  positive: "check-circle-2",
@@ -469,18 +470,19 @@ var STATUS_ICON = {
469
470
  informative: "info",
470
471
  neutral: "circle"
471
472
  };
472
- function Badge({ children, status = "neutral", showIcon = true, className, ...rest }) {
473
- return /* @__PURE__ */ jsxs4("span", { className: cx("pire-badge", className), "data-status": status, ...rest, children: [
473
+ var Badge = React4.forwardRef(function Badge2({ children, status = "neutral", showIcon = true, className, ...rest }, ref) {
474
+ return /* @__PURE__ */ jsxs4("span", { ref, className: cx("pire-badge", className), "data-status": status, ...rest, children: [
474
475
  showIcon ? /* @__PURE__ */ jsx5(Icon, { name: STATUS_ICON[status], size: 12 }) : null,
475
476
  children
476
477
  ] });
477
- }
478
+ });
478
479
 
479
480
  // src/components/core/Tag.tsx
481
+ import * as React5 from "react";
480
482
  import { Button as AriaButton3 } from "react-aria-components";
481
483
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
482
- function Tag({ children, onRemove, isDisabled, category, className, ...rest }) {
483
- return /* @__PURE__ */ jsxs5("span", { className: cx("pire-tag", className), "data-disabled": isDisabled ? "true" : void 0, ...rest, children: [
484
+ var Tag = React5.forwardRef(function Tag2({ children, onRemove, isDisabled, category, className, ...rest }, ref) {
485
+ return /* @__PURE__ */ jsxs5("span", { ref, className: cx("pire-tag", className), "data-disabled": isDisabled ? "true" : void 0, ...rest, children: [
484
486
  category ? /* @__PURE__ */ jsx6("span", { className: "pire-tag-swatch", "data-category": category, "aria-hidden": "true" }) : null,
485
487
  children,
486
488
  onRemove ? /* @__PURE__ */ jsx6(
@@ -494,7 +496,7 @@ function Tag({ children, onRemove, isDisabled, category, className, ...rest }) {
494
496
  }
495
497
  ) : null
496
498
  ] });
497
- }
499
+ });
498
500
 
499
501
  // src/components/core/Card.tsx
500
502
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
@@ -514,11 +516,11 @@ function Card({ title, subtitle, icon, actions, footer, interactive, children, c
514
516
  }
515
517
 
516
518
  // src/components/core/Avatar.tsx
517
- import * as React4 from "react";
519
+ import * as React6 from "react";
518
520
  import { jsx as jsx8 } from "react/jsx-runtime";
519
521
  var initials = (name) => name.trim().split(/\s+/).slice(0, 2).map((p) => p[0]).join("").toUpperCase();
520
522
  function Avatar({ name, size = "md", src, className, ...rest }) {
521
- const [failed, setFailed] = React4.useState(false);
523
+ const [failed, setFailed] = React6.useState(false);
522
524
  return /* @__PURE__ */ jsx8("span", { className: cx("pire-avatar", className), "data-size": size, role: "img", "aria-label": name, ...rest, children: src && !failed ? /* @__PURE__ */ jsx8(
523
525
  "img",
524
526
  {
@@ -531,7 +533,7 @@ function Avatar({ name, size = "md", src, className, ...rest }) {
531
533
  }
532
534
 
533
535
  // src/components/core/SegmentedControl.tsx
534
- import * as React5 from "react";
536
+ import * as React7 from "react";
535
537
  import { ToggleButtonGroup, ToggleButton } from "react-aria-components";
536
538
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
537
539
  function SegmentedControl({
@@ -545,7 +547,7 @@ function SegmentedControl({
545
547
  style,
546
548
  ...rest
547
549
  }) {
548
- const selected = React5.useMemo(() => new Set(value ? [value] : defaultValue ? [defaultValue] : []), [value, defaultValue]);
550
+ const selected = React7.useMemo(() => new Set(value ? [value] : defaultValue ? [defaultValue] : []), [value, defaultValue]);
549
551
  return /* @__PURE__ */ jsx9(
550
552
  ToggleButtonGroup,
551
553
  {
@@ -579,8 +581,9 @@ function SegmentedControl({
579
581
  }
580
582
 
581
583
  // src/components/core/Text.tsx
584
+ import * as React8 from "react";
582
585
  import { jsx as jsx10 } from "react/jsx-runtime";
583
- function Text({
586
+ var Text = React8.forwardRef(function Text2({
584
587
  size = "body",
585
588
  color = "primary",
586
589
  weight = "regular",
@@ -591,12 +594,13 @@ function Text({
591
594
  className,
592
595
  style,
593
596
  ...rest
594
- }) {
597
+ }, ref) {
595
598
  const lineClamp = typeof truncate === "number" ? truncate : truncate ? 1 : void 0;
596
599
  const clampStyle = lineClamp == null ? style : { ["--pire-text-clamp"]: lineClamp, ...style };
597
600
  return /* @__PURE__ */ jsx10(
598
601
  Wrapper,
599
602
  {
603
+ ref,
600
604
  className: cx("pire-text", className),
601
605
  "data-size": size,
602
606
  "data-color": color,
@@ -608,10 +612,10 @@ function Text({
608
612
  ...rest
609
613
  }
610
614
  );
611
- }
612
- function Strong({ className, ...rest }) {
613
- return /* @__PURE__ */ jsx10(Text, { elementType: "span", weight: "strong", className, ...rest });
614
- }
615
+ });
616
+ var Strong = React8.forwardRef(function Strong2({ className, ...rest }, ref) {
617
+ return /* @__PURE__ */ jsx10(Text, { ref, elementType: "span", weight: "strong", className, ...rest });
618
+ });
615
619
 
616
620
  // src/components/core/Heading.tsx
617
621
  import { Heading as AriaHeading } from "react-aria-components";
@@ -707,19 +711,19 @@ function FormField({ label, htmlFor, isRequired, hint, error, layout = "stacked"
707
711
  }
708
712
 
709
713
  // src/components/forms/Input.tsx
710
- import * as React7 from "react";
714
+ import * as React10 from "react";
711
715
  import {
712
716
  TextField,
713
717
  Label,
714
718
  Input as AriaInput,
715
719
  Group,
716
- Text as Text2,
720
+ Text as Text3,
717
721
  FieldError,
718
722
  Button as AriaButton4
719
723
  } from "react-aria-components";
720
724
 
721
725
  // src/i18n/PireIntlProvider.tsx
722
- import * as React6 from "react";
726
+ import * as React9 from "react";
723
727
 
724
728
  // src/i18n/messages.ts
725
729
  var enMessages = {
@@ -730,6 +734,7 @@ var enMessages = {
730
734
  dataTableSelectRow: "Select row",
731
735
  dataTableLoading: "Loading records",
732
736
  dataTableSummaryRow: "Summary",
737
+ dataTableEmpty: "No records match the current filters.",
733
738
  dialogClose: "Close",
734
739
  sidePanelClose: "Close panel",
735
740
  inlineMessageDismiss: "Dismiss message",
@@ -789,6 +794,7 @@ var esMessages = {
789
794
  dataTableSelectRow: "Seleccionar fila",
790
795
  dataTableLoading: "Cargando registros",
791
796
  dataTableSummaryRow: "Resumen",
797
+ dataTableEmpty: "Ning\xFAn registro coincide con los filtros actuales.",
792
798
  dialogClose: "Cerrar",
793
799
  sidePanelClose: "Cerrar panel",
794
800
  inlineMessageDismiss: "Descartar mensaje",
@@ -843,19 +849,19 @@ var esMessages = {
843
849
 
844
850
  // src/i18n/PireIntlProvider.tsx
845
851
  import { jsx as jsx16 } from "react/jsx-runtime";
846
- var MessagesContext = React6.createContext(enMessages);
852
+ var MessagesContext = React9.createContext(enMessages);
847
853
  function PireIntlProvider({ messages, children }) {
848
- const value = React6.useMemo(
854
+ const value = React9.useMemo(
849
855
  () => messages ? { ...enMessages, ...messages } : enMessages,
850
856
  [messages]
851
857
  );
852
858
  return /* @__PURE__ */ jsx16(MessagesContext.Provider, { value, children });
853
859
  }
854
860
  function usePireMessages() {
855
- return React6.useContext(MessagesContext);
861
+ return React9.useContext(MessagesContext);
856
862
  }
857
863
  function useMessage(key, override) {
858
- const messages = React6.useContext(MessagesContext);
864
+ const messages = React9.useContext(MessagesContext);
859
865
  return override ?? messages[key];
860
866
  }
861
867
 
@@ -880,7 +886,7 @@ function Input({
880
886
  }) {
881
887
  const msg = usePireMessages();
882
888
  const isControlled = rest.value !== void 0;
883
- const [ownValue, setOwnValue] = React7.useState(rest.defaultValue ?? "");
889
+ const [ownValue, setOwnValue] = React10.useState(rest.defaultValue ?? "");
884
890
  const currentValue = isControlled ? rest.value : ownValue;
885
891
  const handleChange = (next) => {
886
892
  setOwnValue(next);
@@ -919,7 +925,7 @@ function Input({
919
925
  ]
920
926
  }
921
927
  ),
922
- description ? /* @__PURE__ */ jsx17(Text2, { slot: "description", className: "pire-field-hint", children: description }) : null,
928
+ description ? /* @__PURE__ */ jsx17(Text3, { slot: "description", className: "pire-field-hint", children: description }) : null,
923
929
  /* @__PURE__ */ jsx17(FieldError, { className: "pire-field-error", children: errorMessage })
924
930
  ]
925
931
  }
@@ -927,10 +933,10 @@ function Input({
927
933
  }
928
934
 
929
935
  // src/components/forms/TextArea.tsx
930
- import * as React8 from "react";
931
- import { TextField as TextField2, Label as Label2, TextArea as AriaTextArea, Group as Group2, Text as Text3, FieldError as FieldError2 } from "react-aria-components";
936
+ import * as React11 from "react";
937
+ import { TextField as TextField2, Label as Label2, TextArea as AriaTextArea, Group as Group2, Text as Text4, FieldError as FieldError2 } from "react-aria-components";
932
938
  import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
933
- var useIsomorphicLayoutEffect = typeof document === "undefined" ? React8.useEffect : React8.useLayoutEffect;
939
+ var useIsomorphicLayoutEffect = typeof document === "undefined" ? React11.useEffect : React11.useLayoutEffect;
934
940
  function TextArea({
935
941
  label,
936
942
  description,
@@ -946,13 +952,13 @@ function TextArea({
946
952
  onChange,
947
953
  ...rest
948
954
  }) {
949
- const innerRef = React8.useRef(null);
950
- const ref = React8.useCallback((node) => {
955
+ const innerRef = React11.useRef(null);
956
+ const ref = React11.useCallback((node) => {
951
957
  innerRef.current = node;
952
958
  if (typeof textAreaRef === "function") textAreaRef(node);
953
959
  else if (textAreaRef) textAreaRef.current = node;
954
960
  }, [textAreaRef]);
955
- const grow = React8.useCallback(() => {
961
+ const grow = React11.useCallback(() => {
956
962
  const el = innerRef.current;
957
963
  if (!el || !autoGrow) return;
958
964
  const styles = getComputedStyle(el);
@@ -1004,7 +1010,7 @@ function TextArea({
1004
1010
  )
1005
1011
  }
1006
1012
  ),
1007
- description ? /* @__PURE__ */ jsx18(Text3, { slot: "description", className: "pire-field-hint", children: description }) : null,
1013
+ description ? /* @__PURE__ */ jsx18(Text4, { slot: "description", className: "pire-field-hint", children: description }) : null,
1008
1014
  /* @__PURE__ */ jsx18(FieldError2, { className: "pire-field-error", children: errorMessage })
1009
1015
  ]
1010
1016
  }
@@ -1020,7 +1026,7 @@ import {
1020
1026
  Popover,
1021
1027
  ListBox,
1022
1028
  ListBoxItem,
1023
- Text as Text4,
1029
+ Text as Text5,
1024
1030
  FieldError as FieldError3
1025
1031
  } from "react-aria-components";
1026
1032
  import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
@@ -1073,7 +1079,7 @@ function Select({
1073
1079
  ]
1074
1080
  }
1075
1081
  ),
1076
- description ? /* @__PURE__ */ jsx19(Text4, { slot: "description", className: "pire-field-hint", children: description }) : null,
1082
+ description ? /* @__PURE__ */ jsx19(Text5, { slot: "description", className: "pire-field-hint", children: description }) : null,
1077
1083
  /* @__PURE__ */ jsx19(FieldError3, { className: "pire-field-error", children: errorMessage }),
1078
1084
  /* @__PURE__ */ jsx19(Popover, { className: "pire-popover", children: /* @__PURE__ */ jsx19(
1079
1085
  ListBox,
@@ -1107,7 +1113,7 @@ import {
1107
1113
  RadioGroup as AriaRadioGroup,
1108
1114
  Radio as AriaRadio,
1109
1115
  Label as Label4,
1110
- Text as Text5,
1116
+ Text as Text6,
1111
1117
  FieldError as FieldError4
1112
1118
  } from "react-aria-components";
1113
1119
  import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
@@ -1115,7 +1121,7 @@ function RadioGroup({ label, description, errorMessage, children, className, ...
1115
1121
  return /* @__PURE__ */ jsxs14(AriaRadioGroup, { className: cx("pire-field", className), validationBehavior: "aria", ...rest, children: [
1116
1122
  label ? /* @__PURE__ */ jsx21(Label4, { className: "pire-field-label", children: label }) : null,
1117
1123
  /* @__PURE__ */ jsx21("div", { className: "pire-radiogroup", children }),
1118
- description ? /* @__PURE__ */ jsx21(Text5, { slot: "description", className: "pire-field-hint", children: description }) : null,
1124
+ description ? /* @__PURE__ */ jsx21(Text6, { slot: "description", className: "pire-field-hint", children: description }) : null,
1119
1125
  /* @__PURE__ */ jsx21(FieldError4, { className: "pire-field-error", children: errorMessage })
1120
1126
  ] });
1121
1127
  }
@@ -1140,6 +1146,7 @@ function Switch({ label, children, className, ...rest }) {
1140
1146
  }
1141
1147
 
1142
1148
  // src/components/forms/ComboBox.tsx
1149
+ import * as React12 from "react";
1143
1150
  import {
1144
1151
  ComboBox as AriaComboBox,
1145
1152
  Label as Label5,
@@ -1149,7 +1156,7 @@ import {
1149
1156
  Popover as Popover2,
1150
1157
  ListBox as ListBox2,
1151
1158
  ListBoxItem as ListBoxItem2,
1152
- Text as Text6,
1159
+ Text as Text7,
1153
1160
  FieldError as FieldError5
1154
1161
  } from "react-aria-components";
1155
1162
  import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
@@ -1170,6 +1177,8 @@ function ComboBox({
1170
1177
  isLoading,
1171
1178
  loadingLabel,
1172
1179
  emptyLabel,
1180
+ isClearable,
1181
+ clearLabel,
1173
1182
  onLoadMore,
1174
1183
  className,
1175
1184
  style,
@@ -1177,6 +1186,23 @@ function ComboBox({
1177
1186
  }) {
1178
1187
  const msg = usePireMessages();
1179
1188
  const items = options.map(norm2);
1189
+ const isSelectionControlled = value !== void 0;
1190
+ const [ownSelection, setOwnSelection] = React12.useState(defaultValue ?? null);
1191
+ const takesOverSelection = isClearable === true && !isSelectionControlled;
1192
+ const currentSelection = isSelectionControlled ? value : ownSelection;
1193
+ const [typedText, setTypedText] = React12.useState("");
1194
+ const currentText = inputValue !== void 0 ? inputValue : typedText;
1195
+ const handleInputChange = isClearable ? (text) => {
1196
+ setTypedText(text);
1197
+ onInputChange?.(text);
1198
+ } : onInputChange;
1199
+ const showsClear = isClearable && !rest.isDisabled && !rest.isReadOnly && (currentText !== "" || currentSelection != null);
1200
+ const clear = () => {
1201
+ setTypedText("");
1202
+ if (takesOverSelection) setOwnSelection(null);
1203
+ onChange?.(null);
1204
+ onInputChange?.("");
1205
+ };
1180
1206
  const collection = isFilteredExternally ? { items } : { defaultItems: items };
1181
1207
  const showsStatus = isLoading || isFilteredExternally && items.length === 0 && emptyLabel != null;
1182
1208
  return /* @__PURE__ */ jsxs16(
@@ -1184,14 +1210,16 @@ function ComboBox({
1184
1210
  {
1185
1211
  className: cx("pire-field", className),
1186
1212
  style,
1187
- selectedKey: value,
1188
- defaultSelectedKey: defaultValue,
1213
+ selectedKey: takesOverSelection ? ownSelection : value,
1214
+ defaultSelectedKey: takesOverSelection ? void 0 : defaultValue,
1189
1215
  onSelectionChange: (k) => {
1190
1216
  if (isLoading) return;
1191
- onChange?.(k == null ? null : String(k));
1217
+ const next = k == null ? null : String(k);
1218
+ if (takesOverSelection) setOwnSelection(next);
1219
+ onChange?.(next);
1192
1220
  },
1193
1221
  inputValue,
1194
- onInputChange,
1222
+ onInputChange: handleInputChange,
1195
1223
  ...collection,
1196
1224
  allowsEmptyCollection: isFilteredExternally || isLoading,
1197
1225
  menuTrigger: "input",
@@ -1212,11 +1240,12 @@ function ComboBox({
1212
1240
  children: [
1213
1241
  /* @__PURE__ */ jsx23(Icon, { name: "search", size: 16, className: "pire-leading-icon" }),
1214
1242
  /* @__PURE__ */ jsx23(AriaInput2, { className: "pire-input", placeholder: rest.placeholder }),
1243
+ showsClear ? /* @__PURE__ */ jsx23(AriaButton6, { className: "pire-input-clear", slot: null, onPress: clear, children: clearLabel ?? msg.inputClear }) : null,
1215
1244
  /* @__PURE__ */ jsx23(AriaButton6, { className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.comboBoxShowSuggestions, style: { width: 20, height: 20 }, children: /* @__PURE__ */ jsx23(Icon, { name: "chevron-down", size: 16 }) })
1216
1245
  ]
1217
1246
  }
1218
1247
  ),
1219
- description ? /* @__PURE__ */ jsx23(Text6, { slot: "description", className: "pire-field-hint", children: description }) : null,
1248
+ description ? /* @__PURE__ */ jsx23(Text7, { slot: "description", className: "pire-field-hint", children: description }) : null,
1220
1249
  /* @__PURE__ */ jsx23(FieldError5, { className: "pire-field-error", children: errorMessage }),
1221
1250
  /* @__PURE__ */ jsx23(Popover2, { className: "pire-popover", children: /* @__PURE__ */ jsx23(
1222
1251
  ListBox2,
@@ -1239,7 +1268,7 @@ function ComboBox({
1239
1268
  }
1240
1269
 
1241
1270
  // src/components/forms/MultiComboBox.tsx
1242
- import * as React9 from "react";
1271
+ import * as React13 from "react";
1243
1272
  import {
1244
1273
  ComboBox as AriaComboBox2,
1245
1274
  Label as Label6,
@@ -1250,7 +1279,7 @@ import {
1250
1279
  Popover as Popover3,
1251
1280
  ListBox as ListBox3,
1252
1281
  ListBoxItem as ListBoxItem3,
1253
- Text as Text7,
1282
+ Text as Text8,
1254
1283
  FieldError as FieldError6,
1255
1284
  VisuallyHidden
1256
1285
  } from "react-aria-components";
@@ -1278,15 +1307,15 @@ function MultiComboBox({
1278
1307
  ...rest
1279
1308
  }) {
1280
1309
  const msg = usePireMessages();
1281
- const items = React9.useMemo(() => options.map(norm3), [options]);
1310
+ const items = React13.useMemo(() => options.map(norm3), [options]);
1282
1311
  const collection = isFilteredExternally ? { items } : { defaultItems: items };
1283
1312
  const showsStatus = isLoading || isFilteredExternally && items.length === 0 && emptyLabel != null;
1284
- const [uncontrolled, setUncontrolled] = React9.useState(defaultValue ?? []);
1285
- const [announcement, setAnnouncement] = React9.useState("");
1286
- const inputRef = React9.useRef(null);
1313
+ const [uncontrolled, setUncontrolled] = React13.useState(defaultValue ?? []);
1314
+ const [announcement, setAnnouncement] = React13.useState("");
1315
+ const inputRef = React13.useRef(null);
1287
1316
  const raw = value ?? uncontrolled;
1288
1317
  const contentKey = raw.join("\0");
1289
- const values = React9.useMemo(() => raw, [contentKey]);
1318
+ const values = React13.useMemo(() => raw, [contentKey]);
1290
1319
  const labelOf = (v) => items.find((o) => o.value === v)?.label ?? v;
1291
1320
  const isEditable = !rest.isDisabled && !rest.isReadOnly;
1292
1321
  const commit = (next) => {
@@ -1372,7 +1401,7 @@ function MultiComboBox({
1372
1401
  }
1373
1402
  ),
1374
1403
  /* @__PURE__ */ jsx24(VisuallyHidden, { children: /* @__PURE__ */ jsx24("span", { "aria-live": "polite", children: announcement }) }),
1375
- description ? /* @__PURE__ */ jsx24(Text7, { slot: "description", className: "pire-field-hint", children: description }) : null,
1404
+ description ? /* @__PURE__ */ jsx24(Text8, { slot: "description", className: "pire-field-hint", children: description }) : null,
1376
1405
  /* @__PURE__ */ jsx24(FieldError6, { className: "pire-field-error", children: errorMessage }),
1377
1406
  /* @__PURE__ */ jsx24(Popover3, { className: "pire-popover", children: /* @__PURE__ */ jsx24(
1378
1407
  ListBox3,
@@ -1401,10 +1430,32 @@ import {
1401
1430
  Input as AriaInput4,
1402
1431
  Group as Group5,
1403
1432
  Button as AriaButton8,
1404
- Text as Text8,
1405
- FieldError as FieldError7
1433
+ Text as Text9,
1434
+ FieldError as FieldError7,
1435
+ useLocale
1406
1436
  } from "react-aria-components";
1407
1437
  import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
1438
+ function decimalSeparatorFor(locale) {
1439
+ return new Intl.NumberFormat(locale).formatToParts(1.1).find((part) => part.type === "decimal")?.value ?? ".";
1440
+ }
1441
+ function typeIntoInput(el, text) {
1442
+ const start = el.selectionStart ?? el.value.length;
1443
+ const end = el.selectionEnd ?? start;
1444
+ const setValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
1445
+ setValue?.call(el, el.value.slice(0, start) + text + el.value.slice(end));
1446
+ el.dispatchEvent(new Event("input", { bubbles: true }));
1447
+ const caret = start + text.length;
1448
+ el.setSelectionRange(caret, caret);
1449
+ }
1450
+ function toAriaNumber(value) {
1451
+ if (value === void 0) {
1452
+ return void 0;
1453
+ }
1454
+ if (value === null || value === "") {
1455
+ return NaN;
1456
+ }
1457
+ return typeof value === "number" ? value : Number(value);
1458
+ }
1408
1459
  function NumberField({
1409
1460
  label,
1410
1461
  suffix,
@@ -1415,35 +1466,72 @@ function NumberField({
1415
1466
  fullWidth = true,
1416
1467
  className,
1417
1468
  style,
1469
+ value,
1470
+ defaultValue,
1471
+ onChange,
1472
+ emptyValue,
1418
1473
  ...rest
1419
1474
  }) {
1420
1475
  const msg = usePireMessages();
1421
- return /* @__PURE__ */ jsxs18(AriaNumberField, { className: cx("pire-field", className), style, validationBehavior: "aria", ...rest, children: [
1422
- label ? /* @__PURE__ */ jsxs18(Label7, { className: "pire-field-label", children: [
1423
- label,
1424
- rest.isRequired ? /* @__PURE__ */ jsx25("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
1425
- ] }) : null,
1426
- /* @__PURE__ */ jsxs18(
1427
- Group5,
1428
- {
1429
- className: "pire-control",
1430
- "data-size": size,
1431
- "data-full-width": String(fullWidth),
1432
- "data-invalid": rest.isInvalid ? "true" : void 0,
1433
- "data-readonly": rest.isReadOnly ? "true" : void 0,
1434
- children: [
1435
- /* @__PURE__ */ jsx25(AriaInput4, { className: "pire-input", "data-numeric": "true" }),
1436
- suffix ? /* @__PURE__ */ jsx25("span", { className: "pire-affix", children: suffix }) : null,
1437
- hideStepper ? null : /* @__PURE__ */ jsxs18("span", { className: "pire-stepper", children: [
1438
- /* @__PURE__ */ jsx25(AriaButton8, { slot: "increment", className: "pire-stepper-btn", "aria-label": msg.numberFieldIncrease, children: /* @__PURE__ */ jsx25(Icon, { name: "plus", size: 10 }) }),
1439
- /* @__PURE__ */ jsx25(AriaButton8, { slot: "decrement", className: "pire-stepper-btn", "aria-label": msg.numberFieldDecrease, children: /* @__PURE__ */ jsx25(Icon, { name: "minus", size: 10 }) })
1440
- ] })
1441
- ]
1442
- }
1443
- ),
1444
- description ? /* @__PURE__ */ jsx25(Text8, { slot: "description", className: "pire-field-hint", children: description }) : null,
1445
- /* @__PURE__ */ jsx25(FieldError7, { className: "pire-field-error", children: errorMessage })
1446
- ] });
1476
+ const { locale } = useLocale();
1477
+ const empty = emptyValue === void 0 ? NaN : emptyValue;
1478
+ const handleChange = onChange && ((next) => onChange(Number.isNaN(next) ? empty : next));
1479
+ const handleDecimalKey = (event) => {
1480
+ if (event.key !== "." || event.ctrlKey || event.metaKey || event.altKey) {
1481
+ return;
1482
+ }
1483
+ if (rest.isReadOnly || rest.isDisabled) {
1484
+ return;
1485
+ }
1486
+ const separator = decimalSeparatorFor(locale);
1487
+ if (separator === ".") {
1488
+ return;
1489
+ }
1490
+ const { maximumFractionDigits } = new Intl.NumberFormat(locale, rest.formatOptions).resolvedOptions();
1491
+ if (!maximumFractionDigits) {
1492
+ return;
1493
+ }
1494
+ event.preventDefault();
1495
+ typeIntoInput(event.currentTarget, separator);
1496
+ };
1497
+ return /* @__PURE__ */ jsxs18(
1498
+ AriaNumberField,
1499
+ {
1500
+ className: cx("pire-field", className),
1501
+ style,
1502
+ validationBehavior: "aria",
1503
+ value: toAriaNumber(value),
1504
+ defaultValue: toAriaNumber(defaultValue),
1505
+ onChange: handleChange,
1506
+ ...rest,
1507
+ children: [
1508
+ label ? /* @__PURE__ */ jsxs18(Label7, { className: "pire-field-label", children: [
1509
+ label,
1510
+ rest.isRequired ? /* @__PURE__ */ jsx25("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
1511
+ ] }) : null,
1512
+ /* @__PURE__ */ jsxs18(
1513
+ Group5,
1514
+ {
1515
+ className: "pire-control",
1516
+ "data-size": size,
1517
+ "data-full-width": String(fullWidth),
1518
+ "data-invalid": rest.isInvalid ? "true" : void 0,
1519
+ "data-readonly": rest.isReadOnly ? "true" : void 0,
1520
+ children: [
1521
+ /* @__PURE__ */ jsx25(AriaInput4, { className: "pire-input", "data-numeric": "true", onKeyDown: handleDecimalKey }),
1522
+ suffix ? /* @__PURE__ */ jsx25("span", { className: "pire-affix", children: suffix }) : null,
1523
+ hideStepper ? null : /* @__PURE__ */ jsxs18("span", { className: "pire-stepper", children: [
1524
+ /* @__PURE__ */ jsx25(AriaButton8, { slot: "increment", className: "pire-stepper-btn", "aria-label": msg.numberFieldIncrease, children: /* @__PURE__ */ jsx25(Icon, { name: "plus", size: 10 }) }),
1525
+ /* @__PURE__ */ jsx25(AriaButton8, { slot: "decrement", className: "pire-stepper-btn", "aria-label": msg.numberFieldDecrease, children: /* @__PURE__ */ jsx25(Icon, { name: "minus", size: 10 }) })
1526
+ ] })
1527
+ ]
1528
+ }
1529
+ ),
1530
+ description ? /* @__PURE__ */ jsx25(Text9, { slot: "description", className: "pire-field-hint", children: description }) : null,
1531
+ /* @__PURE__ */ jsx25(FieldError7, { className: "pire-field-error", children: errorMessage })
1532
+ ]
1533
+ }
1534
+ );
1447
1535
  }
1448
1536
 
1449
1537
  // src/components/forms/DatePicker.tsx
@@ -1463,10 +1551,32 @@ import {
1463
1551
  CalendarGridBody,
1464
1552
  CalendarCell,
1465
1553
  Heading as Heading2,
1466
- Text as Text9,
1554
+ Text as Text10,
1467
1555
  FieldError as FieldError8
1468
1556
  } from "react-aria-components";
1557
+ import { parseDate } from "@internationalized/date";
1469
1558
  import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
1559
+ var ISO_DATE = /^(\d{4}-\d{2}-\d{2})(?:[T ].*)?$/;
1560
+ function coerceDateValue(value) {
1561
+ if (value === void 0) {
1562
+ return void 0;
1563
+ }
1564
+ if (value === null || value === "") {
1565
+ return null;
1566
+ }
1567
+ if (typeof value !== "string") {
1568
+ return value;
1569
+ }
1570
+ const match = ISO_DATE.exec(value);
1571
+ if (!match) {
1572
+ return null;
1573
+ }
1574
+ try {
1575
+ return parseDate(match[1]);
1576
+ } catch {
1577
+ return null;
1578
+ }
1579
+ }
1470
1580
  function DatePicker({
1471
1581
  label,
1472
1582
  description,
@@ -1475,42 +1585,61 @@ function DatePicker({
1475
1585
  fullWidth = true,
1476
1586
  className,
1477
1587
  style,
1588
+ value,
1589
+ defaultValue,
1590
+ onChange,
1591
+ valueFormat,
1478
1592
  ...rest
1479
1593
  }) {
1480
1594
  const msg = usePireMessages();
1481
- return /* @__PURE__ */ jsxs19(AriaDatePicker, { className: cx("pire-field", className), style, validationBehavior: "aria", ...rest, children: [
1482
- label ? /* @__PURE__ */ jsxs19(Label8, { className: "pire-field-label", children: [
1483
- label,
1484
- rest.isRequired ? /* @__PURE__ */ jsx26("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
1485
- ] }) : null,
1486
- /* @__PURE__ */ jsxs19(
1487
- Group6,
1488
- {
1489
- className: "pire-control",
1490
- "data-size": size,
1491
- "data-full-width": String(fullWidth),
1492
- "data-invalid": rest.isInvalid ? "true" : void 0,
1493
- "data-readonly": rest.isReadOnly ? "true" : void 0,
1494
- children: [
1495
- /* @__PURE__ */ jsx26(DateInput, { className: "pire-datefield", children: (segment) => /* @__PURE__ */ jsx26(DateSegment, { segment, className: "pire-datesegment" }) }),
1496
- /* @__PURE__ */ jsx26(AriaButton9, { className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerOpenCalendar, style: { width: 24, height: 24 }, children: /* @__PURE__ */ jsx26(Icon, { name: "calendar", size: 16 }) })
1497
- ]
1498
- }
1499
- ),
1500
- description ? /* @__PURE__ */ jsx26(Text9, { slot: "description", className: "pire-field-hint", children: description }) : null,
1501
- /* @__PURE__ */ jsx26(FieldError8, { className: "pire-field-error", children: errorMessage }),
1502
- /* @__PURE__ */ jsx26(Popover4, { className: "pire-popover", children: /* @__PURE__ */ jsx26(AriaDialog, { children: /* @__PURE__ */ jsxs19(Calendar, { className: "pire-calendar", children: [
1503
- /* @__PURE__ */ jsxs19("header", { className: "pire-calendar-head", children: [
1504
- /* @__PURE__ */ jsx26(AriaButton9, { slot: "previous", className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerPreviousMonth, children: /* @__PURE__ */ jsx26(Icon, { name: "chevron-left", size: 16 }) }),
1505
- /* @__PURE__ */ jsx26(Heading2, { className: "pire-calendar-title" }),
1506
- /* @__PURE__ */ jsx26(AriaButton9, { slot: "next", className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerNextMonth, children: /* @__PURE__ */ jsx26(Icon, { name: "chevron-right", size: 16 }) })
1507
- ] }),
1508
- /* @__PURE__ */ jsxs19(CalendarGrid, { className: "pire-calendar-grid", children: [
1509
- /* @__PURE__ */ jsx26(CalendarGridHeader, { children: (day) => /* @__PURE__ */ jsx26(CalendarHeaderCell, { children: day }) }),
1510
- /* @__PURE__ */ jsx26(CalendarGridBody, { children: (date) => /* @__PURE__ */ jsx26(CalendarCell, { date, className: "pire-calendar-cell" }) })
1511
- ] })
1512
- ] }) }) })
1513
- ] });
1595
+ const handleChange = onChange && ((next) => onChange(
1596
+ valueFormat === "iso" && next ? next.toString() : next
1597
+ ));
1598
+ return /* @__PURE__ */ jsxs19(
1599
+ AriaDatePicker,
1600
+ {
1601
+ className: cx("pire-field", className),
1602
+ style,
1603
+ validationBehavior: "aria",
1604
+ value: coerceDateValue(value),
1605
+ defaultValue: coerceDateValue(defaultValue),
1606
+ onChange: handleChange,
1607
+ ...rest,
1608
+ children: [
1609
+ label ? /* @__PURE__ */ jsxs19(Label8, { className: "pire-field-label", children: [
1610
+ label,
1611
+ rest.isRequired ? /* @__PURE__ */ jsx26("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
1612
+ ] }) : null,
1613
+ /* @__PURE__ */ jsxs19(
1614
+ Group6,
1615
+ {
1616
+ className: "pire-control",
1617
+ "data-size": size,
1618
+ "data-full-width": String(fullWidth),
1619
+ "data-invalid": rest.isInvalid ? "true" : void 0,
1620
+ "data-readonly": rest.isReadOnly ? "true" : void 0,
1621
+ children: [
1622
+ /* @__PURE__ */ jsx26(DateInput, { className: "pire-datefield", children: (segment) => /* @__PURE__ */ jsx26(DateSegment, { segment, className: "pire-datesegment" }) }),
1623
+ /* @__PURE__ */ jsx26(AriaButton9, { className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerOpenCalendar, style: { width: 24, height: 24 }, children: /* @__PURE__ */ jsx26(Icon, { name: "calendar", size: 16 }) })
1624
+ ]
1625
+ }
1626
+ ),
1627
+ description ? /* @__PURE__ */ jsx26(Text10, { slot: "description", className: "pire-field-hint", children: description }) : null,
1628
+ /* @__PURE__ */ jsx26(FieldError8, { className: "pire-field-error", children: errorMessage }),
1629
+ /* @__PURE__ */ jsx26(Popover4, { className: "pire-popover", children: /* @__PURE__ */ jsx26(AriaDialog, { children: /* @__PURE__ */ jsxs19(Calendar, { className: "pire-calendar", children: [
1630
+ /* @__PURE__ */ jsxs19("header", { className: "pire-calendar-head", children: [
1631
+ /* @__PURE__ */ jsx26(AriaButton9, { slot: "previous", className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerPreviousMonth, children: /* @__PURE__ */ jsx26(Icon, { name: "chevron-left", size: 16 }) }),
1632
+ /* @__PURE__ */ jsx26(Heading2, { className: "pire-calendar-title" }),
1633
+ /* @__PURE__ */ jsx26(AriaButton9, { slot: "next", className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.datePickerNextMonth, children: /* @__PURE__ */ jsx26(Icon, { name: "chevron-right", size: 16 }) })
1634
+ ] }),
1635
+ /* @__PURE__ */ jsxs19(CalendarGrid, { className: "pire-calendar-grid", children: [
1636
+ /* @__PURE__ */ jsx26(CalendarGridHeader, { children: (day) => /* @__PURE__ */ jsx26(CalendarHeaderCell, { children: day }) }),
1637
+ /* @__PURE__ */ jsx26(CalendarGridBody, { children: (date) => /* @__PURE__ */ jsx26(CalendarCell, { date, className: "pire-calendar-cell" }) })
1638
+ ] })
1639
+ ] }) }) })
1640
+ ]
1641
+ }
1642
+ );
1514
1643
  }
1515
1644
 
1516
1645
  // src/components/forms/DateRangePicker.tsx
@@ -1530,7 +1659,7 @@ import {
1530
1659
  CalendarGridBody as CalendarGridBody2,
1531
1660
  CalendarCell as CalendarCell2,
1532
1661
  Heading as Heading3,
1533
- Text as Text10,
1662
+ Text as Text11,
1534
1663
  FieldError as FieldError9
1535
1664
  } from "react-aria-components";
1536
1665
  import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
@@ -1594,7 +1723,7 @@ function DateRangePicker({
1594
1723
  ]
1595
1724
  }
1596
1725
  ),
1597
- description ? /* @__PURE__ */ jsx27(Text10, { slot: "description", className: "pire-field-hint", children: description }) : null,
1726
+ description ? /* @__PURE__ */ jsx27(Text11, { slot: "description", className: "pire-field-hint", children: description }) : null,
1598
1727
  /* @__PURE__ */ jsx27(FieldError9, { className: "pire-field-error", children: errorMessage }),
1599
1728
  /* @__PURE__ */ jsx27(Popover5, { className: "pire-popover", children: /* @__PURE__ */ jsxs20(AriaDialog2, { className: "pire-daterange-dialog", children: [
1600
1729
  presets?.length ? (
@@ -1646,13 +1775,13 @@ function DateRangePicker({
1646
1775
  }
1647
1776
 
1648
1777
  // src/components/forms/ValueHelpField.tsx
1649
- import * as React12 from "react";
1778
+ import * as React16 from "react";
1650
1779
 
1651
1780
  // src/components/patterns/ValueHelpDialog.tsx
1652
- import * as React11 from "react";
1781
+ import * as React15 from "react";
1653
1782
 
1654
1783
  // src/components/feedback/Dialog.tsx
1655
- import * as React10 from "react";
1784
+ import * as React14 from "react";
1656
1785
  import { ModalOverlay, Modal, Dialog as AriaDialog3, Heading as Heading4 } from "react-aria-components";
1657
1786
  import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
1658
1787
  function DialogTitle({ children, className }) {
@@ -1686,8 +1815,8 @@ function Dialog({
1686
1815
  onClose?.();
1687
1816
  onOpenChange?.(false);
1688
1817
  };
1689
- const parts = React10.Children.toArray(children);
1690
- const slotOf = (type) => parts.find((node) => React10.isValidElement(node) && node.type === type);
1818
+ const parts = React14.Children.toArray(children);
1819
+ const slotOf = (type) => parts.find((node) => React14.isValidElement(node) && node.type === type);
1691
1820
  const titleSlot = slotOf(DialogTitle);
1692
1821
  const descriptionSlot = slotOf(DialogDescription);
1693
1822
  const bodySlot = slotOf(DialogBody);
@@ -1791,7 +1920,7 @@ function DataTable({
1791
1920
  onSortChange,
1792
1921
  onRowAction,
1793
1922
  stickyHeader = true,
1794
- emptyLabel = "No records match the current filters.",
1923
+ emptyLabel,
1795
1924
  isLoading,
1796
1925
  loadingRowCount,
1797
1926
  loadingLabel,
@@ -1804,6 +1933,8 @@ function DataTable({
1804
1933
  ...rest
1805
1934
  }) {
1806
1935
  const msg = usePireMessages();
1936
+ const selectionMode = selectable === true || selectable === "multiple" ? "multiple" : selectable === "single" ? "single" : "none";
1937
+ const hasCheckboxColumn = selectionMode === "multiple";
1807
1938
  const sortDescriptor = sort ? { column: sort.key, direction: sort.dir === "asc" ? "ascending" : "descending" } : void 0;
1808
1939
  const handleSelection = (keys) => {
1809
1940
  if (keys === "all") return onSelectionChange?.(rows.map((r) => r.id));
@@ -1811,14 +1942,14 @@ function DataTable({
1811
1942
  };
1812
1943
  const skeletonRows = loadingRowCount ?? (rows.length || 5);
1813
1944
  const skeletonColumns = [
1814
- ...selectable ? [{ width: 44 }] : [],
1945
+ ...hasCheckboxColumn ? [{ width: 44 }] : [],
1815
1946
  ...columns.map((c) => ({ width: c.width, align: c.align, numeric: c.numeric }))
1816
1947
  ];
1817
1948
  const body = isLoading ? [] : rows;
1818
1949
  const presentation = new Map(rows.map((row) => [row.id, rowProps?.(row) ?? {}]));
1819
1950
  const disabledKeys = rows.filter((row) => presentation.get(row.id)?.isDisabled).map((row) => row.id);
1820
1951
  const expanded = new Set((expandedIds ?? []).map(String));
1821
- const spanAll = columns.length + (selectable ? 1 : 0);
1952
+ const spanAll = columns.length + (hasCheckboxColumn ? 1 : 0);
1822
1953
  const expandedKey = (id) => `${id}::__pire_expanded__`;
1823
1954
  return /* @__PURE__ */ jsx30(
1824
1955
  "div",
@@ -1835,7 +1966,7 @@ function DataTable({
1835
1966
  "data-sticky": String(stickyHeader),
1836
1967
  "aria-label": rest["aria-label"],
1837
1968
  disabledKeys: disabledKeys.length ? new Set(disabledKeys) : void 0,
1838
- selectionMode: selectable ? "multiple" : "none",
1969
+ selectionMode,
1839
1970
  selectedKeys: selected ? new Set(selected) : void 0,
1840
1971
  onSelectionChange: handleSelection,
1841
1972
  sortDescriptor,
@@ -1846,13 +1977,13 @@ function DataTable({
1846
1977
  } : void 0,
1847
1978
  children: [
1848
1979
  /* @__PURE__ */ jsxs22(TableHeader, { className: "pire-thead-row", children: [
1849
- selectable ? /* @__PURE__ */ jsx30(Column, { className: "pire-th", width: 44, children: /* @__PURE__ */ jsx30(Checkbox, { slot: "selection", "aria-label": msg.dataTableSelectAll }) }) : null,
1980
+ hasCheckboxColumn ? /* @__PURE__ */ jsx30(Column, { className: "pire-th", width: 44, children: /* @__PURE__ */ jsx30(Checkbox, { slot: "selection", "aria-label": msg.dataTableSelectAll }) }) : null,
1850
1981
  columns.map((c, i) => /* @__PURE__ */ jsx30(
1851
1982
  Column,
1852
1983
  {
1853
1984
  id: c.key,
1854
1985
  className: "pire-th",
1855
- isRowHeader: i === 0 && !selectable,
1986
+ isRowHeader: i === 0 && !hasCheckboxColumn,
1856
1987
  allowsSorting: c.sortable,
1857
1988
  width: c.width,
1858
1989
  "data-align": c.numeric || c.align === "right" ? "right" : c.align,
@@ -1878,7 +2009,7 @@ function DataTable({
1878
2009
  // biome-ignore lint/suspicious/noArrayIndexKey: placeholder rows are positional.
1879
2010
  /* @__PURE__ */ jsx30(SkeletonTableRow, { columns: skeletonColumns, density }, i)
1880
2011
  ))
1881
- ] }) : /* @__PURE__ */ jsx30("div", { className: "pire-table-empty", children: emptyLabel }), children: body.flatMap((row) => {
2012
+ ] }) : /* @__PURE__ */ jsx30("div", { className: "pire-table-empty", children: emptyLabel ?? msg.dataTableEmpty }), children: body.flatMap((row) => {
1882
2013
  const rowIsDisabled = presentation.get(row.id)?.isDisabled;
1883
2014
  const record = /* @__PURE__ */ jsxs22(
1884
2015
  Row,
@@ -1887,7 +2018,7 @@ function DataTable({
1887
2018
  className: cx("pire-tr", presentation.get(row.id)?.className),
1888
2019
  "data-pressable": onRowAction && !rowIsDisabled ? "true" : void 0,
1889
2020
  children: [
1890
- selectable ? /* @__PURE__ */ jsx30(Cell, { className: "pire-td", children: /* @__PURE__ */ jsx30(Checkbox, { slot: "selection", "aria-label": msg.dataTableSelectRow }) }) : null,
2021
+ hasCheckboxColumn ? /* @__PURE__ */ jsx30(Cell, { className: "pire-td", children: /* @__PURE__ */ jsx30(Checkbox, { slot: "selection", "aria-label": msg.dataTableSelectRow }) }) : null,
1891
2022
  columns.map((c) => /* @__PURE__ */ jsx30(
1892
2023
  Cell,
1893
2024
  {
@@ -1906,14 +2037,14 @@ function DataTable({
1906
2037
  return [record, /* @__PURE__ */ jsx30(Row, { id: expandedKey(row.id), className: "pire-tr-expanded", children: /* @__PURE__ */ jsx30(Cell, { className: "pire-td-expanded", colSpan: spanAll, children: renderExpanded(row) }) }, expandedKey(row.id))];
1907
2038
  }) }),
1908
2039
  footer && !isLoading ? /* @__PURE__ */ jsx30(TableFooter, { className: "pire-tfoot", children: /* @__PURE__ */ jsxs22(Row, { id: "__pire_footer__", className: "pire-tr-footer", children: [
1909
- selectable ? /* @__PURE__ */ jsx30(Cell, { className: "pire-tf" }) : null,
2040
+ hasCheckboxColumn ? /* @__PURE__ */ jsx30(Cell, { className: "pire-tf" }) : null,
1910
2041
  columns.map((c, i) => /* @__PURE__ */ jsx30(
1911
2042
  Cell,
1912
2043
  {
1913
2044
  className: "pire-tf",
1914
2045
  "data-numeric": c.numeric ? "true" : void 0,
1915
2046
  style: { textAlign: c.align && !c.numeric ? c.align : void 0 },
1916
- children: footer[c.key] ?? (i === 0 && !selectable ? /* @__PURE__ */ jsx30("span", { className: "pire-sr-only", children: msg.dataTableSummaryRow }) : null)
2047
+ children: footer[c.key] ?? (i === 0 && !hasCheckboxColumn ? /* @__PURE__ */ jsx30("span", { className: "pire-sr-only", children: msg.dataTableSummaryRow }) : null)
1917
2048
  },
1918
2049
  c.key
1919
2050
  ))
@@ -1942,11 +2073,11 @@ function ValueHelpDialog({
1942
2073
  onClose
1943
2074
  }) {
1944
2075
  const msg = usePireMessages();
1945
- const [query, setQuery] = React11.useState("");
1946
- React11.useEffect(() => {
2076
+ const [query, setQuery] = React15.useState("");
2077
+ React15.useEffect(() => {
1947
2078
  if (isOpen) setQuery("");
1948
2079
  }, [isOpen]);
1949
- const filtered = React11.useMemo(() => {
2080
+ const filtered = React15.useMemo(() => {
1950
2081
  if (isFilteredExternally) return rows;
1951
2082
  const q = query.trim().toLowerCase();
1952
2083
  if (!q) return rows;
@@ -2028,7 +2159,7 @@ function ValueHelpField({
2028
2159
  style
2029
2160
  }) {
2030
2161
  const msg = usePireMessages();
2031
- const [open, setOpen] = React12.useState(false);
2162
+ const [open, setOpen] = React16.useState(false);
2032
2163
  return /* @__PURE__ */ jsxs24("div", { className: cx(className), style, children: [
2033
2164
  /* @__PURE__ */ jsx32(
2034
2165
  Input,
@@ -2084,8 +2215,8 @@ function ValueHelpField({
2084
2215
  }
2085
2216
 
2086
2217
  // src/components/forms/FileUpload.tsx
2087
- import * as React13 from "react";
2088
- import { FileTrigger, Text as Text11 } from "react-aria-components";
2218
+ import * as React17 from "react";
2219
+ import { FileTrigger, Text as Text12 } from "react-aria-components";
2089
2220
 
2090
2221
  // src/components/feedback/ProgressBar.tsx
2091
2222
  import { ProgressBar as AriaProgressBar, Label as Label10 } from "react-aria-components";
@@ -2171,13 +2302,14 @@ function FileUpload({
2171
2302
  isRequired,
2172
2303
  fullWidth = true,
2173
2304
  className,
2174
- style
2305
+ style,
2306
+ ...rest
2175
2307
  }) {
2176
2308
  const msg = usePireMessages();
2177
- const [uncontrolled, setUncontrolled] = React13.useState(defaultValue ?? []);
2309
+ const [uncontrolled, setUncontrolled] = React17.useState(defaultValue ?? []);
2178
2310
  const files = value ?? uncontrolled;
2179
- const created = React13.useRef([]);
2180
- React13.useEffect(() => () => {
2311
+ const created = React17.useRef([]);
2312
+ React17.useEffect(() => () => {
2181
2313
  for (const url of created.current) URL.revokeObjectURL(url);
2182
2314
  }, []);
2183
2315
  const commit = (next) => {
@@ -2204,6 +2336,7 @@ function FileUpload({
2204
2336
  style,
2205
2337
  "data-full-width": String(fullWidth),
2206
2338
  "data-invalid": isInvalid ? "true" : void 0,
2339
+ ...rest,
2207
2340
  children: [
2208
2341
  label ? /* @__PURE__ */ jsxs26("span", { className: "pire-field-label", children: [
2209
2342
  label,
@@ -2233,7 +2366,7 @@ function FileUpload({
2233
2366
  )
2234
2367
  ] }, `${file.name}-${file.size}-${file.lastModified}`)) }) : null,
2235
2368
  uploadProgress != null ? /* @__PURE__ */ jsx34(ProgressBar, { value: uploadProgress, label: msg.fileUploadUploading }) : null,
2236
- description ? /* @__PURE__ */ jsx34(Text11, { slot: "description", className: "pire-field-hint", children: description }) : null,
2369
+ description ? /* @__PURE__ */ jsx34(Text12, { slot: "description", className: "pire-field-hint", children: description }) : null,
2237
2370
  isInvalid && errorMessage ? /* @__PURE__ */ jsx34("span", { className: "pire-field-error", children: errorMessage }) : null
2238
2371
  ]
2239
2372
  }
@@ -2288,7 +2421,7 @@ function ShellBar({
2288
2421
  }
2289
2422
 
2290
2423
  // src/components/navigation/SideNav.tsx
2291
- import * as React14 from "react";
2424
+ import * as React18 from "react";
2292
2425
  import {
2293
2426
  Button as AriaButton12,
2294
2427
  Disclosure,
@@ -2388,10 +2521,10 @@ function SideNav({
2388
2521
  const msg = usePireMessages();
2389
2522
  const activeParent = findActiveParent(groups, value);
2390
2523
  const isControlled = expandedIds != null;
2391
- const [ownExpanded, setOwnExpanded] = React14.useState(
2524
+ const [ownExpanded, setOwnExpanded] = React18.useState(
2392
2525
  () => new Set(defaultExpandedIds ?? (activeParent ? [activeParent] : []))
2393
2526
  );
2394
- const [lastValue, setLastValue] = React14.useState(value);
2527
+ const [lastValue, setLastValue] = React18.useState(value);
2395
2528
  if (value !== lastValue) {
2396
2529
  setLastValue(value);
2397
2530
  if (!isControlled && activeParent && !ownExpanded.has(activeParent)) {
@@ -2655,7 +2788,7 @@ function InlineMessage({ kind = "info", title, action, onClose, children, classN
2655
2788
  }
2656
2789
 
2657
2790
  // src/components/feedback/Toast.tsx
2658
- import * as React15 from "react";
2791
+ import * as React19 from "react";
2659
2792
  import { jsx as jsx42, jsxs as jsxs34 } from "react/jsx-runtime";
2660
2793
  var KIND_ICON2 = {
2661
2794
  info: "info",
@@ -2681,26 +2814,26 @@ function Toast({ kind = "success", onClose, position = "bottom-center", children
2681
2814
  }
2682
2815
  );
2683
2816
  }
2684
- var ToastContext = React15.createContext(null);
2817
+ var ToastContext = React19.createContext(null);
2685
2818
  function ToastProvider({ children, timeout = 6e3 }) {
2686
2819
  const msg = usePireMessages();
2687
- const [toasts, setToasts] = React15.useState([]);
2688
- const close = React15.useCallback((id) => setToasts((t) => t.filter((x) => x.id !== id)), []);
2689
- const add = React15.useCallback((toast) => {
2820
+ const [toasts, setToasts] = React19.useState([]);
2821
+ const close = React19.useCallback((id) => setToasts((t) => t.filter((x) => x.id !== id)), []);
2822
+ const add = React19.useCallback((toast) => {
2690
2823
  const id = Math.random().toString(36).slice(2);
2691
2824
  setToasts((t) => [...t, { ...toast, id }]);
2692
2825
  const ms = toast.timeout ?? timeout;
2693
2826
  if (ms > 0) setTimeout(() => close(id), ms);
2694
2827
  return id;
2695
2828
  }, [close, timeout]);
2696
- const value = React15.useMemo(() => ({ add, close }), [add, close]);
2829
+ const value = React19.useMemo(() => ({ add, close }), [add, close]);
2697
2830
  return /* @__PURE__ */ jsxs34(ToastContext.Provider, { value, children: [
2698
2831
  children,
2699
2832
  /* @__PURE__ */ jsx42("div", { className: "pire-toast-region", role: "region", "aria-label": msg.toastRegionLabel, children: toasts.map((t) => /* @__PURE__ */ jsx42(Toast, { kind: t.kind, onClose: () => close(t.id), style: { position: "static", transform: "none" }, children: t.message }, t.id)) })
2700
2833
  ] });
2701
2834
  }
2702
2835
  function useToast() {
2703
- const ctx = React15.useContext(ToastContext);
2836
+ const ctx = React19.useContext(ToastContext);
2704
2837
  if (!ctx) throw new Error("useToast must be used inside <ToastProvider>");
2705
2838
  return ctx;
2706
2839
  }
@@ -3050,14 +3183,14 @@ function BarChart({
3050
3183
  }
3051
3184
 
3052
3185
  // src/components/data/LineChart.tsx
3053
- import * as React16 from "react";
3186
+ import * as React20 from "react";
3054
3187
  import { jsx as jsx50, jsxs as jsxs42 } from "react/jsx-runtime";
3055
3188
  var PLOT_INSET = { top: 10, right: 14, bottom: 24, left: 58 };
3056
3189
  var MARKER_LIMIT = 20;
3057
- var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? React16.useEffect : React16.useLayoutEffect;
3190
+ var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? React20.useEffect : React20.useLayoutEffect;
3058
3191
  function useMeasuredWidth() {
3059
- const ref = React16.useRef(null);
3060
- const [width, setWidth] = React16.useState(0);
3192
+ const ref = React20.useRef(null);
3193
+ const [width, setWidth] = React20.useState(0);
3061
3194
  useIsomorphicLayoutEffect2(() => {
3062
3195
  const element = ref.current;
3063
3196
  if (!element) return;
@@ -3220,7 +3353,7 @@ function LineChart({
3220
3353
  }
3221
3354
 
3222
3355
  // src/components/data/DonutChart.tsx
3223
- import * as React17 from "react";
3356
+ import * as React21 from "react";
3224
3357
  import { jsx as jsx51, jsxs as jsxs43 } from "react/jsx-runtime";
3225
3358
  var SEGMENT_GAP_DEGREES = 1.5;
3226
3359
  function DonutChart({
@@ -3241,7 +3374,7 @@ function DonutChart({
3241
3374
  }) {
3242
3375
  const label = rest["aria-label"];
3243
3376
  const msg = usePireMessages();
3244
- const [hoveredIndex, setHoveredIndex] = React17.useState(null);
3377
+ const [hoveredIndex, setHoveredIndex] = React21.useState(null);
3245
3378
  warnOnDonutShape(series, categories.length, label);
3246
3379
  const values = (series[0]?.values ?? []).slice(0, categories.length);
3247
3380
  const segments = categories.map((category, index) => ({ category, index, value: values[index] ?? 0 })).filter((segment) => segment.value > 0);
@@ -3478,7 +3611,7 @@ function ConfirmDialog({
3478
3611
  }
3479
3612
 
3480
3613
  // src/components/patterns/CommandPalette.tsx
3481
- import * as React18 from "react";
3614
+ import * as React22 from "react";
3482
3615
  import {
3483
3616
  Autocomplete,
3484
3617
  ModalOverlay as ModalOverlay2,
@@ -3512,17 +3645,17 @@ function CommandPalette({
3512
3645
  style
3513
3646
  }) {
3514
3647
  const msg = usePireMessages();
3515
- const [query, setQuery] = React18.useState("");
3648
+ const [query, setQuery] = React22.useState("");
3516
3649
  const { contains } = useFilter({ sensitivity: "base" });
3517
- React18.useEffect(() => {
3650
+ React22.useEffect(() => {
3518
3651
  if (isOpen) setQuery("");
3519
3652
  }, [isOpen]);
3520
- const searchableById = React18.useMemo(() => {
3653
+ const searchableById = React22.useMemo(() => {
3521
3654
  const byId = /* @__PURE__ */ new Map();
3522
3655
  for (const group of groups) for (const item of group.items) byId.set(item.id, searchableText(item));
3523
3656
  return byId;
3524
3657
  }, [groups]);
3525
- const filter = React18.useCallback((textValue, inputValue, node) => {
3658
+ const filter = React22.useCallback((textValue, inputValue, node) => {
3526
3659
  if (node.parentKey === RESULTS_SECTION_KEY) return true;
3527
3660
  return contains(searchableById.get(String(node.key)) ?? textValue, inputValue);
3528
3661
  }, [contains, searchableById]);
@@ -3605,9 +3738,9 @@ function CommandPalette({
3605
3738
  );
3606
3739
  }
3607
3740
  function useCommandPaletteShortcut(onTrigger, { key = "k", isDisabled } = {}) {
3608
- const handler = React18.useRef(onTrigger);
3741
+ const handler = React22.useRef(onTrigger);
3609
3742
  handler.current = onTrigger;
3610
- React18.useEffect(() => {
3743
+ React22.useEffect(() => {
3611
3744
  if (isDisabled) return;
3612
3745
  const onKeyDown = (event) => {
3613
3746
  if (!(event.metaKey || event.ctrlKey) || event.altKey) return;
@@ -3661,7 +3794,7 @@ function EmptyState({ icon = "file-text", title, action, compact, children, clas
3661
3794
  }
3662
3795
 
3663
3796
  // src/components/patterns/FileDropZone.tsx
3664
- import * as React19 from "react";
3797
+ import * as React23 from "react";
3665
3798
  import { DropZone, FileTrigger as FileTrigger2, isFileDropItem } from "react-aria-components";
3666
3799
  import { jsx as jsx63, jsxs as jsxs54 } from "react/jsx-runtime";
3667
3800
  var isImage2 = (file) => file.type.startsWith("image/");
@@ -3686,13 +3819,15 @@ function FileDropZone({
3686
3819
  isRequired,
3687
3820
  fullWidth = true,
3688
3821
  className,
3689
- style
3822
+ style,
3823
+ "aria-label": ariaLabel,
3824
+ ...rest
3690
3825
  }) {
3691
3826
  const msg = usePireMessages();
3692
- const [uncontrolled, setUncontrolled] = React19.useState(defaultValue ?? []);
3827
+ const [uncontrolled, setUncontrolled] = React23.useState(defaultValue ?? []);
3693
3828
  const files = value ?? uncontrolled;
3694
- const created = React19.useRef([]);
3695
- React19.useEffect(() => () => {
3829
+ const created = React23.useRef([]);
3830
+ React23.useEffect(() => () => {
3696
3831
  for (const url of created.current) URL.revokeObjectURL(url);
3697
3832
  }, []);
3698
3833
  const commit = (next) => {
@@ -3712,7 +3847,7 @@ function FileDropZone({
3712
3847
  commit(allowsMultiple ? [...files, ...wrapped] : wrapped.slice(0, 1));
3713
3848
  };
3714
3849
  const remove = (target) => commit(files.filter((f) => f.file !== target));
3715
- return /* @__PURE__ */ jsxs54("div", { className: cx("pire-field", className), style, "data-full-width": String(fullWidth), children: [
3850
+ return /* @__PURE__ */ jsxs54("div", { className: cx("pire-field", className), style, "data-full-width": String(fullWidth), ...rest, children: [
3716
3851
  label ? /* @__PURE__ */ jsxs54("span", { className: "pire-field-label", children: [
3717
3852
  label,
3718
3853
  isRequired ? /* @__PURE__ */ jsx63("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
@@ -3721,6 +3856,7 @@ function FileDropZone({
3721
3856
  DropZone,
3722
3857
  {
3723
3858
  className: "pire-dropzone",
3859
+ "aria-label": ariaLabel,
3724
3860
  "data-height": height,
3725
3861
  isDisabled: isDisabled || isReadOnly,
3726
3862
  onDrop: async (e) => {