@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.cjs CHANGED
@@ -586,6 +586,7 @@ var IconButton = React3.forwardRef(function IconButton2({
586
586
  });
587
587
 
588
588
  // src/components/core/Badge.tsx
589
+ var React4 = __toESM(require("react"), 1);
589
590
  var import_jsx_runtime5 = require("react/jsx-runtime");
590
591
  var STATUS_ICON = {
591
592
  positive: "check-circle-2",
@@ -594,18 +595,19 @@ var STATUS_ICON = {
594
595
  informative: "info",
595
596
  neutral: "circle"
596
597
  };
597
- function Badge({ children, status = "neutral", showIcon = true, className, ...rest }) {
598
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: cx("pire-badge", className), "data-status": status, ...rest, children: [
598
+ var Badge = React4.forwardRef(function Badge2({ children, status = "neutral", showIcon = true, className, ...rest }, ref) {
599
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { ref, className: cx("pire-badge", className), "data-status": status, ...rest, children: [
599
600
  showIcon ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { name: STATUS_ICON[status], size: 12 }) : null,
600
601
  children
601
602
  ] });
602
- }
603
+ });
603
604
 
604
605
  // src/components/core/Tag.tsx
606
+ var React5 = __toESM(require("react"), 1);
605
607
  var import_react_aria_components4 = require("react-aria-components");
606
608
  var import_jsx_runtime6 = require("react/jsx-runtime");
607
- function Tag({ children, onRemove, isDisabled, category, className, ...rest }) {
608
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: cx("pire-tag", className), "data-disabled": isDisabled ? "true" : void 0, ...rest, children: [
609
+ var Tag = React5.forwardRef(function Tag2({ children, onRemove, isDisabled, category, className, ...rest }, ref) {
610
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { ref, className: cx("pire-tag", className), "data-disabled": isDisabled ? "true" : void 0, ...rest, children: [
609
611
  category ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "pire-tag-swatch", "data-category": category, "aria-hidden": "true" }) : null,
610
612
  children,
611
613
  onRemove ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -619,7 +621,7 @@ function Tag({ children, onRemove, isDisabled, category, className, ...rest }) {
619
621
  }
620
622
  ) : null
621
623
  ] });
622
- }
624
+ });
623
625
 
624
626
  // src/components/core/Card.tsx
625
627
  var import_jsx_runtime7 = require("react/jsx-runtime");
@@ -639,11 +641,11 @@ function Card({ title, subtitle, icon, actions, footer, interactive, children, c
639
641
  }
640
642
 
641
643
  // src/components/core/Avatar.tsx
642
- var React4 = __toESM(require("react"), 1);
644
+ var React6 = __toESM(require("react"), 1);
643
645
  var import_jsx_runtime8 = require("react/jsx-runtime");
644
646
  var initials = (name) => name.trim().split(/\s+/).slice(0, 2).map((p) => p[0]).join("").toUpperCase();
645
647
  function Avatar({ name, size = "md", src, className, ...rest }) {
646
- const [failed, setFailed] = React4.useState(false);
648
+ const [failed, setFailed] = React6.useState(false);
647
649
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: cx("pire-avatar", className), "data-size": size, role: "img", "aria-label": name, ...rest, children: src && !failed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
648
650
  "img",
649
651
  {
@@ -656,7 +658,7 @@ function Avatar({ name, size = "md", src, className, ...rest }) {
656
658
  }
657
659
 
658
660
  // src/components/core/SegmentedControl.tsx
659
- var React5 = __toESM(require("react"), 1);
661
+ var React7 = __toESM(require("react"), 1);
660
662
  var import_react_aria_components5 = require("react-aria-components");
661
663
  var import_jsx_runtime9 = require("react/jsx-runtime");
662
664
  function SegmentedControl({
@@ -670,7 +672,7 @@ function SegmentedControl({
670
672
  style,
671
673
  ...rest
672
674
  }) {
673
- const selected = React5.useMemo(() => new Set(value ? [value] : defaultValue ? [defaultValue] : []), [value, defaultValue]);
675
+ const selected = React7.useMemo(() => new Set(value ? [value] : defaultValue ? [defaultValue] : []), [value, defaultValue]);
674
676
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
675
677
  import_react_aria_components5.ToggleButtonGroup,
676
678
  {
@@ -704,8 +706,9 @@ function SegmentedControl({
704
706
  }
705
707
 
706
708
  // src/components/core/Text.tsx
709
+ var React8 = __toESM(require("react"), 1);
707
710
  var import_jsx_runtime10 = require("react/jsx-runtime");
708
- function Text({
711
+ var Text = React8.forwardRef(function Text2({
709
712
  size = "body",
710
713
  color = "primary",
711
714
  weight = "regular",
@@ -716,12 +719,13 @@ function Text({
716
719
  className,
717
720
  style,
718
721
  ...rest
719
- }) {
722
+ }, ref) {
720
723
  const lineClamp = typeof truncate === "number" ? truncate : truncate ? 1 : void 0;
721
724
  const clampStyle = lineClamp == null ? style : { ["--pire-text-clamp"]: lineClamp, ...style };
722
725
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
723
726
  Wrapper,
724
727
  {
728
+ ref,
725
729
  className: cx("pire-text", className),
726
730
  "data-size": size,
727
731
  "data-color": color,
@@ -733,10 +737,10 @@ function Text({
733
737
  ...rest
734
738
  }
735
739
  );
736
- }
737
- function Strong({ className, ...rest }) {
738
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { elementType: "span", weight: "strong", className, ...rest });
739
- }
740
+ });
741
+ var Strong = React8.forwardRef(function Strong2({ className, ...rest }, ref) {
742
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { ref, elementType: "span", weight: "strong", className, ...rest });
743
+ });
740
744
 
741
745
  // src/components/core/Heading.tsx
742
746
  var import_react_aria_components6 = require("react-aria-components");
@@ -832,11 +836,11 @@ function FormField({ label, htmlFor, isRequired, hint, error, layout = "stacked"
832
836
  }
833
837
 
834
838
  // src/components/forms/Input.tsx
835
- var React7 = __toESM(require("react"), 1);
839
+ var React10 = __toESM(require("react"), 1);
836
840
  var import_react_aria_components9 = require("react-aria-components");
837
841
 
838
842
  // src/i18n/PireIntlProvider.tsx
839
- var React6 = __toESM(require("react"), 1);
843
+ var React9 = __toESM(require("react"), 1);
840
844
 
841
845
  // src/i18n/messages.ts
842
846
  var enMessages = {
@@ -847,6 +851,7 @@ var enMessages = {
847
851
  dataTableSelectRow: "Select row",
848
852
  dataTableLoading: "Loading records",
849
853
  dataTableSummaryRow: "Summary",
854
+ dataTableEmpty: "No records match the current filters.",
850
855
  dialogClose: "Close",
851
856
  sidePanelClose: "Close panel",
852
857
  inlineMessageDismiss: "Dismiss message",
@@ -906,6 +911,7 @@ var esMessages = {
906
911
  dataTableSelectRow: "Seleccionar fila",
907
912
  dataTableLoading: "Cargando registros",
908
913
  dataTableSummaryRow: "Resumen",
914
+ dataTableEmpty: "Ning\xFAn registro coincide con los filtros actuales.",
909
915
  dialogClose: "Cerrar",
910
916
  sidePanelClose: "Cerrar panel",
911
917
  inlineMessageDismiss: "Descartar mensaje",
@@ -960,19 +966,19 @@ var esMessages = {
960
966
 
961
967
  // src/i18n/PireIntlProvider.tsx
962
968
  var import_jsx_runtime16 = require("react/jsx-runtime");
963
- var MessagesContext = React6.createContext(enMessages);
969
+ var MessagesContext = React9.createContext(enMessages);
964
970
  function PireIntlProvider({ messages, children }) {
965
- const value = React6.useMemo(
971
+ const value = React9.useMemo(
966
972
  () => messages ? { ...enMessages, ...messages } : enMessages,
967
973
  [messages]
968
974
  );
969
975
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MessagesContext.Provider, { value, children });
970
976
  }
971
977
  function usePireMessages() {
972
- return React6.useContext(MessagesContext);
978
+ return React9.useContext(MessagesContext);
973
979
  }
974
980
  function useMessage(key, override) {
975
- const messages = React6.useContext(MessagesContext);
981
+ const messages = React9.useContext(MessagesContext);
976
982
  return override ?? messages[key];
977
983
  }
978
984
 
@@ -997,7 +1003,7 @@ function Input({
997
1003
  }) {
998
1004
  const msg = usePireMessages();
999
1005
  const isControlled = rest.value !== void 0;
1000
- const [ownValue, setOwnValue] = React7.useState(rest.defaultValue ?? "");
1006
+ const [ownValue, setOwnValue] = React10.useState(rest.defaultValue ?? "");
1001
1007
  const currentValue = isControlled ? rest.value : ownValue;
1002
1008
  const handleChange = (next) => {
1003
1009
  setOwnValue(next);
@@ -1044,10 +1050,10 @@ function Input({
1044
1050
  }
1045
1051
 
1046
1052
  // src/components/forms/TextArea.tsx
1047
- var React8 = __toESM(require("react"), 1);
1053
+ var React11 = __toESM(require("react"), 1);
1048
1054
  var import_react_aria_components10 = require("react-aria-components");
1049
1055
  var import_jsx_runtime18 = require("react/jsx-runtime");
1050
- var useIsomorphicLayoutEffect = typeof document === "undefined" ? React8.useEffect : React8.useLayoutEffect;
1056
+ var useIsomorphicLayoutEffect = typeof document === "undefined" ? React11.useEffect : React11.useLayoutEffect;
1051
1057
  function TextArea({
1052
1058
  label,
1053
1059
  description,
@@ -1063,13 +1069,13 @@ function TextArea({
1063
1069
  onChange,
1064
1070
  ...rest
1065
1071
  }) {
1066
- const innerRef = React8.useRef(null);
1067
- const ref = React8.useCallback((node) => {
1072
+ const innerRef = React11.useRef(null);
1073
+ const ref = React11.useCallback((node) => {
1068
1074
  innerRef.current = node;
1069
1075
  if (typeof textAreaRef === "function") textAreaRef(node);
1070
1076
  else if (textAreaRef) textAreaRef.current = node;
1071
1077
  }, [textAreaRef]);
1072
- const grow = React8.useCallback(() => {
1078
+ const grow = React11.useCallback(() => {
1073
1079
  const el = innerRef.current;
1074
1080
  if (!el || !autoGrow) return;
1075
1081
  const styles = getComputedStyle(el);
@@ -1241,6 +1247,7 @@ function Switch({ label, children, className, ...rest }) {
1241
1247
  }
1242
1248
 
1243
1249
  // src/components/forms/ComboBox.tsx
1250
+ var React12 = __toESM(require("react"), 1);
1244
1251
  var import_react_aria_components15 = require("react-aria-components");
1245
1252
  var import_jsx_runtime23 = require("react/jsx-runtime");
1246
1253
  var norm2 = (o) => typeof o === "string" ? { value: o, label: o } : o;
@@ -1260,6 +1267,8 @@ function ComboBox({
1260
1267
  isLoading,
1261
1268
  loadingLabel,
1262
1269
  emptyLabel,
1270
+ isClearable,
1271
+ clearLabel,
1263
1272
  onLoadMore,
1264
1273
  className,
1265
1274
  style,
@@ -1267,6 +1276,23 @@ function ComboBox({
1267
1276
  }) {
1268
1277
  const msg = usePireMessages();
1269
1278
  const items = options.map(norm2);
1279
+ const isSelectionControlled = value !== void 0;
1280
+ const [ownSelection, setOwnSelection] = React12.useState(defaultValue ?? null);
1281
+ const takesOverSelection = isClearable === true && !isSelectionControlled;
1282
+ const currentSelection = isSelectionControlled ? value : ownSelection;
1283
+ const [typedText, setTypedText] = React12.useState("");
1284
+ const currentText = inputValue !== void 0 ? inputValue : typedText;
1285
+ const handleInputChange = isClearable ? (text) => {
1286
+ setTypedText(text);
1287
+ onInputChange?.(text);
1288
+ } : onInputChange;
1289
+ const showsClear = isClearable && !rest.isDisabled && !rest.isReadOnly && (currentText !== "" || currentSelection != null);
1290
+ const clear = () => {
1291
+ setTypedText("");
1292
+ if (takesOverSelection) setOwnSelection(null);
1293
+ onChange?.(null);
1294
+ onInputChange?.("");
1295
+ };
1270
1296
  const collection = isFilteredExternally ? { items } : { defaultItems: items };
1271
1297
  const showsStatus = isLoading || isFilteredExternally && items.length === 0 && emptyLabel != null;
1272
1298
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
@@ -1274,14 +1300,16 @@ function ComboBox({
1274
1300
  {
1275
1301
  className: cx("pire-field", className),
1276
1302
  style,
1277
- selectedKey: value,
1278
- defaultSelectedKey: defaultValue,
1303
+ selectedKey: takesOverSelection ? ownSelection : value,
1304
+ defaultSelectedKey: takesOverSelection ? void 0 : defaultValue,
1279
1305
  onSelectionChange: (k) => {
1280
1306
  if (isLoading) return;
1281
- onChange?.(k == null ? null : String(k));
1307
+ const next = k == null ? null : String(k);
1308
+ if (takesOverSelection) setOwnSelection(next);
1309
+ onChange?.(next);
1282
1310
  },
1283
1311
  inputValue,
1284
- onInputChange,
1312
+ onInputChange: handleInputChange,
1285
1313
  ...collection,
1286
1314
  allowsEmptyCollection: isFilteredExternally || isLoading,
1287
1315
  menuTrigger: "input",
@@ -1302,6 +1330,7 @@ function ComboBox({
1302
1330
  children: [
1303
1331
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Icon, { name: "search", size: 16, className: "pire-leading-icon" }),
1304
1332
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_aria_components15.Input, { className: "pire-input", placeholder: rest.placeholder }),
1333
+ showsClear ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_aria_components15.Button, { className: "pire-input-clear", slot: null, onPress: clear, children: clearLabel ?? msg.inputClear }) : null,
1305
1334
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_aria_components15.Button, { className: "pire-iconbtn", "data-size": "sm", "aria-label": msg.comboBoxShowSuggestions, style: { width: 20, height: 20 }, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Icon, { name: "chevron-down", size: 16 }) })
1306
1335
  ]
1307
1336
  }
@@ -1329,7 +1358,7 @@ function ComboBox({
1329
1358
  }
1330
1359
 
1331
1360
  // src/components/forms/MultiComboBox.tsx
1332
- var React9 = __toESM(require("react"), 1);
1361
+ var React13 = __toESM(require("react"), 1);
1333
1362
  var import_react_aria_components16 = require("react-aria-components");
1334
1363
  var import_jsx_runtime24 = require("react/jsx-runtime");
1335
1364
  var norm3 = (o) => typeof o === "string" ? { value: o, label: o } : o;
@@ -1355,15 +1384,15 @@ function MultiComboBox({
1355
1384
  ...rest
1356
1385
  }) {
1357
1386
  const msg = usePireMessages();
1358
- const items = React9.useMemo(() => options.map(norm3), [options]);
1387
+ const items = React13.useMemo(() => options.map(norm3), [options]);
1359
1388
  const collection = isFilteredExternally ? { items } : { defaultItems: items };
1360
1389
  const showsStatus = isLoading || isFilteredExternally && items.length === 0 && emptyLabel != null;
1361
- const [uncontrolled, setUncontrolled] = React9.useState(defaultValue ?? []);
1362
- const [announcement, setAnnouncement] = React9.useState("");
1363
- const inputRef = React9.useRef(null);
1390
+ const [uncontrolled, setUncontrolled] = React13.useState(defaultValue ?? []);
1391
+ const [announcement, setAnnouncement] = React13.useState("");
1392
+ const inputRef = React13.useRef(null);
1364
1393
  const raw = value ?? uncontrolled;
1365
1394
  const contentKey = raw.join("\0");
1366
- const values = React9.useMemo(() => raw, [contentKey]);
1395
+ const values = React13.useMemo(() => raw, [contentKey]);
1367
1396
  const labelOf = (v) => items.find((o) => o.value === v)?.label ?? v;
1368
1397
  const isEditable = !rest.isDisabled && !rest.isReadOnly;
1369
1398
  const commit = (next) => {
@@ -1474,6 +1503,27 @@ function MultiComboBox({
1474
1503
  // src/components/forms/NumberField.tsx
1475
1504
  var import_react_aria_components17 = require("react-aria-components");
1476
1505
  var import_jsx_runtime25 = require("react/jsx-runtime");
1506
+ function decimalSeparatorFor(locale) {
1507
+ return new Intl.NumberFormat(locale).formatToParts(1.1).find((part) => part.type === "decimal")?.value ?? ".";
1508
+ }
1509
+ function typeIntoInput(el, text) {
1510
+ const start = el.selectionStart ?? el.value.length;
1511
+ const end = el.selectionEnd ?? start;
1512
+ const setValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
1513
+ setValue?.call(el, el.value.slice(0, start) + text + el.value.slice(end));
1514
+ el.dispatchEvent(new Event("input", { bubbles: true }));
1515
+ const caret = start + text.length;
1516
+ el.setSelectionRange(caret, caret);
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
+ }
1477
1527
  function NumberField({
1478
1528
  label,
1479
1529
  suffix,
@@ -1484,40 +1534,99 @@ function NumberField({
1484
1534
  fullWidth = true,
1485
1535
  className,
1486
1536
  style,
1537
+ value,
1538
+ defaultValue,
1539
+ onChange,
1540
+ emptyValue,
1487
1541
  ...rest
1488
1542
  }) {
1489
1543
  const msg = usePireMessages();
1490
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_aria_components17.NumberField, { className: cx("pire-field", className), style, validationBehavior: "aria", ...rest, children: [
1491
- label ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_aria_components17.Label, { className: "pire-field-label", children: [
1492
- label,
1493
- rest.isRequired ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
1494
- ] }) : null,
1495
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1496
- import_react_aria_components17.Group,
1497
- {
1498
- className: "pire-control",
1499
- "data-size": size,
1500
- "data-full-width": String(fullWidth),
1501
- "data-invalid": rest.isInvalid ? "true" : void 0,
1502
- "data-readonly": rest.isReadOnly ? "true" : void 0,
1503
- children: [
1504
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Input, { className: "pire-input", "data-numeric": "true" }),
1505
- suffix ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "pire-affix", children: suffix }) : null,
1506
- hideStepper ? null : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "pire-stepper", children: [
1507
- /* @__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 }) }),
1508
- /* @__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 }) })
1509
- ] })
1510
- ]
1511
- }
1512
- ),
1513
- description ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Text, { slot: "description", className: "pire-field-hint", children: description }) : null,
1514
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.FieldError, { className: "pire-field-error", children: errorMessage })
1515
- ] });
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));
1547
+ const handleDecimalKey = (event) => {
1548
+ if (event.key !== "." || event.ctrlKey || event.metaKey || event.altKey) {
1549
+ return;
1550
+ }
1551
+ if (rest.isReadOnly || rest.isDisabled) {
1552
+ return;
1553
+ }
1554
+ const separator = decimalSeparatorFor(locale);
1555
+ if (separator === ".") {
1556
+ return;
1557
+ }
1558
+ const { maximumFractionDigits } = new Intl.NumberFormat(locale, rest.formatOptions).resolvedOptions();
1559
+ if (!maximumFractionDigits) {
1560
+ return;
1561
+ }
1562
+ event.preventDefault();
1563
+ typeIntoInput(event.currentTarget, separator);
1564
+ };
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
+ );
1516
1603
  }
1517
1604
 
1518
1605
  // src/components/forms/DatePicker.tsx
1519
1606
  var import_react_aria_components18 = require("react-aria-components");
1607
+ var import_date = require("@internationalized/date");
1520
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
+ }
1521
1630
  function DatePicker({
1522
1631
  label,
1523
1632
  description,
@@ -1526,42 +1635,61 @@ function DatePicker({
1526
1635
  fullWidth = true,
1527
1636
  className,
1528
1637
  style,
1638
+ value,
1639
+ defaultValue,
1640
+ onChange,
1641
+ valueFormat,
1529
1642
  ...rest
1530
1643
  }) {
1531
1644
  const msg = usePireMessages();
1532
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_aria_components18.DatePicker, { className: cx("pire-field", className), style, validationBehavior: "aria", ...rest, children: [
1533
- label ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_aria_components18.Label, { className: "pire-field-label", children: [
1534
- label,
1535
- rest.isRequired ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
1536
- ] }) : null,
1537
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1538
- import_react_aria_components18.Group,
1539
- {
1540
- className: "pire-control",
1541
- "data-size": size,
1542
- "data-full-width": String(fullWidth),
1543
- "data-invalid": rest.isInvalid ? "true" : void 0,
1544
- "data-readonly": rest.isReadOnly ? "true" : void 0,
1545
- children: [
1546
- /* @__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" }) }),
1547
- /* @__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 }) })
1548
- ]
1549
- }
1550
- ),
1551
- description ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Text, { slot: "description", className: "pire-field-hint", children: description }) : null,
1552
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.FieldError, { className: "pire-field-error", children: errorMessage }),
1553
- /* @__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: [
1554
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("header", { className: "pire-calendar-head", children: [
1555
- /* @__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 }) }),
1556
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_aria_components18.Heading, { className: "pire-calendar-title" }),
1557
- /* @__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 }) })
1558
- ] }),
1559
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_aria_components18.CalendarGrid, { className: "pire-calendar-grid", children: [
1560
- /* @__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 }) }),
1561
- /* @__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" }) })
1562
- ] })
1563
- ] }) }) })
1564
- ] });
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
+ );
1565
1693
  }
1566
1694
 
1567
1695
  // src/components/forms/DateRangePicker.tsx
@@ -1679,13 +1807,13 @@ function DateRangePicker({
1679
1807
  }
1680
1808
 
1681
1809
  // src/components/forms/ValueHelpField.tsx
1682
- var React12 = __toESM(require("react"), 1);
1810
+ var React16 = __toESM(require("react"), 1);
1683
1811
 
1684
1812
  // src/components/patterns/ValueHelpDialog.tsx
1685
- var React11 = __toESM(require("react"), 1);
1813
+ var React15 = __toESM(require("react"), 1);
1686
1814
 
1687
1815
  // src/components/feedback/Dialog.tsx
1688
- var React10 = __toESM(require("react"), 1);
1816
+ var React14 = __toESM(require("react"), 1);
1689
1817
  var import_react_aria_components20 = require("react-aria-components");
1690
1818
  var import_jsx_runtime28 = require("react/jsx-runtime");
1691
1819
  function DialogTitle({ children, className }) {
@@ -1719,8 +1847,8 @@ function Dialog({
1719
1847
  onClose?.();
1720
1848
  onOpenChange?.(false);
1721
1849
  };
1722
- const parts = React10.Children.toArray(children);
1723
- const slotOf = (type) => parts.find((node) => React10.isValidElement(node) && node.type === type);
1850
+ const parts = React14.Children.toArray(children);
1851
+ const slotOf = (type) => parts.find((node) => React14.isValidElement(node) && node.type === type);
1724
1852
  const titleSlot = slotOf(DialogTitle);
1725
1853
  const descriptionSlot = slotOf(DialogDescription);
1726
1854
  const bodySlot = slotOf(DialogBody);
@@ -1816,7 +1944,7 @@ function DataTable({
1816
1944
  onSortChange,
1817
1945
  onRowAction,
1818
1946
  stickyHeader = true,
1819
- emptyLabel = "No records match the current filters.",
1947
+ emptyLabel,
1820
1948
  isLoading,
1821
1949
  loadingRowCount,
1822
1950
  loadingLabel,
@@ -1829,6 +1957,8 @@ function DataTable({
1829
1957
  ...rest
1830
1958
  }) {
1831
1959
  const msg = usePireMessages();
1960
+ const selectionMode = selectable === true || selectable === "multiple" ? "multiple" : selectable === "single" ? "single" : "none";
1961
+ const hasCheckboxColumn = selectionMode === "multiple";
1832
1962
  const sortDescriptor = sort ? { column: sort.key, direction: sort.dir === "asc" ? "ascending" : "descending" } : void 0;
1833
1963
  const handleSelection = (keys) => {
1834
1964
  if (keys === "all") return onSelectionChange?.(rows.map((r) => r.id));
@@ -1836,14 +1966,14 @@ function DataTable({
1836
1966
  };
1837
1967
  const skeletonRows = loadingRowCount ?? (rows.length || 5);
1838
1968
  const skeletonColumns = [
1839
- ...selectable ? [{ width: 44 }] : [],
1969
+ ...hasCheckboxColumn ? [{ width: 44 }] : [],
1840
1970
  ...columns.map((c) => ({ width: c.width, align: c.align, numeric: c.numeric }))
1841
1971
  ];
1842
1972
  const body = isLoading ? [] : rows;
1843
1973
  const presentation = new Map(rows.map((row) => [row.id, rowProps?.(row) ?? {}]));
1844
1974
  const disabledKeys = rows.filter((row) => presentation.get(row.id)?.isDisabled).map((row) => row.id);
1845
1975
  const expanded = new Set((expandedIds ?? []).map(String));
1846
- const spanAll = columns.length + (selectable ? 1 : 0);
1976
+ const spanAll = columns.length + (hasCheckboxColumn ? 1 : 0);
1847
1977
  const expandedKey = (id) => `${id}::__pire_expanded__`;
1848
1978
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1849
1979
  "div",
@@ -1860,7 +1990,7 @@ function DataTable({
1860
1990
  "data-sticky": String(stickyHeader),
1861
1991
  "aria-label": rest["aria-label"],
1862
1992
  disabledKeys: disabledKeys.length ? new Set(disabledKeys) : void 0,
1863
- selectionMode: selectable ? "multiple" : "none",
1993
+ selectionMode,
1864
1994
  selectedKeys: selected ? new Set(selected) : void 0,
1865
1995
  onSelectionChange: handleSelection,
1866
1996
  sortDescriptor,
@@ -1871,13 +2001,13 @@ function DataTable({
1871
2001
  } : void 0,
1872
2002
  children: [
1873
2003
  /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react_aria_components21.TableHeader, { className: "pire-thead-row", children: [
1874
- selectable ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_aria_components21.Column, { className: "pire-th", width: 44, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Checkbox, { slot: "selection", "aria-label": msg.dataTableSelectAll }) }) : null,
2004
+ hasCheckboxColumn ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_aria_components21.Column, { className: "pire-th", width: 44, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Checkbox, { slot: "selection", "aria-label": msg.dataTableSelectAll }) }) : null,
1875
2005
  columns.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1876
2006
  import_react_aria_components21.Column,
1877
2007
  {
1878
2008
  id: c.key,
1879
2009
  className: "pire-th",
1880
- isRowHeader: i === 0 && !selectable,
2010
+ isRowHeader: i === 0 && !hasCheckboxColumn,
1881
2011
  allowsSorting: c.sortable,
1882
2012
  width: c.width,
1883
2013
  "data-align": c.numeric || c.align === "right" ? "right" : c.align,
@@ -1903,7 +2033,7 @@ function DataTable({
1903
2033
  // biome-ignore lint/suspicious/noArrayIndexKey: placeholder rows are positional.
1904
2034
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(SkeletonTableRow, { columns: skeletonColumns, density }, i)
1905
2035
  ))
1906
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "pire-table-empty", children: emptyLabel }), children: body.flatMap((row) => {
2036
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "pire-table-empty", children: emptyLabel ?? msg.dataTableEmpty }), children: body.flatMap((row) => {
1907
2037
  const rowIsDisabled = presentation.get(row.id)?.isDisabled;
1908
2038
  const record = /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
1909
2039
  import_react_aria_components21.Row,
@@ -1912,7 +2042,7 @@ function DataTable({
1912
2042
  className: cx("pire-tr", presentation.get(row.id)?.className),
1913
2043
  "data-pressable": onRowAction && !rowIsDisabled ? "true" : void 0,
1914
2044
  children: [
1915
- selectable ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_aria_components21.Cell, { className: "pire-td", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Checkbox, { slot: "selection", "aria-label": msg.dataTableSelectRow }) }) : null,
2045
+ hasCheckboxColumn ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_aria_components21.Cell, { className: "pire-td", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Checkbox, { slot: "selection", "aria-label": msg.dataTableSelectRow }) }) : null,
1916
2046
  columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1917
2047
  import_react_aria_components21.Cell,
1918
2048
  {
@@ -1931,14 +2061,14 @@ function DataTable({
1931
2061
  return [record, /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_aria_components21.Row, { id: expandedKey(row.id), className: "pire-tr-expanded", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_aria_components21.Cell, { className: "pire-td-expanded", colSpan: spanAll, children: renderExpanded(row) }) }, expandedKey(row.id))];
1932
2062
  }) }),
1933
2063
  footer && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_aria_components21.TableFooter, { className: "pire-tfoot", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react_aria_components21.Row, { id: "__pire_footer__", className: "pire-tr-footer", children: [
1934
- selectable ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_aria_components21.Cell, { className: "pire-tf" }) : null,
2064
+ hasCheckboxColumn ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_aria_components21.Cell, { className: "pire-tf" }) : null,
1935
2065
  columns.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1936
2066
  import_react_aria_components21.Cell,
1937
2067
  {
1938
2068
  className: "pire-tf",
1939
2069
  "data-numeric": c.numeric ? "true" : void 0,
1940
2070
  style: { textAlign: c.align && !c.numeric ? c.align : void 0 },
1941
- children: footer[c.key] ?? (i === 0 && !selectable ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "pire-sr-only", children: msg.dataTableSummaryRow }) : null)
2071
+ children: footer[c.key] ?? (i === 0 && !hasCheckboxColumn ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "pire-sr-only", children: msg.dataTableSummaryRow }) : null)
1942
2072
  },
1943
2073
  c.key
1944
2074
  ))
@@ -1967,11 +2097,11 @@ function ValueHelpDialog({
1967
2097
  onClose
1968
2098
  }) {
1969
2099
  const msg = usePireMessages();
1970
- const [query, setQuery] = React11.useState("");
1971
- React11.useEffect(() => {
2100
+ const [query, setQuery] = React15.useState("");
2101
+ React15.useEffect(() => {
1972
2102
  if (isOpen) setQuery("");
1973
2103
  }, [isOpen]);
1974
- const filtered = React11.useMemo(() => {
2104
+ const filtered = React15.useMemo(() => {
1975
2105
  if (isFilteredExternally) return rows;
1976
2106
  const q = query.trim().toLowerCase();
1977
2107
  if (!q) return rows;
@@ -2053,7 +2183,7 @@ function ValueHelpField({
2053
2183
  style
2054
2184
  }) {
2055
2185
  const msg = usePireMessages();
2056
- const [open, setOpen] = React12.useState(false);
2186
+ const [open, setOpen] = React16.useState(false);
2057
2187
  return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cx(className), style, children: [
2058
2188
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
2059
2189
  Input,
@@ -2109,7 +2239,7 @@ function ValueHelpField({
2109
2239
  }
2110
2240
 
2111
2241
  // src/components/forms/FileUpload.tsx
2112
- var React13 = __toESM(require("react"), 1);
2242
+ var React17 = __toESM(require("react"), 1);
2113
2243
  var import_react_aria_components23 = require("react-aria-components");
2114
2244
 
2115
2245
  // src/components/feedback/ProgressBar.tsx
@@ -2196,13 +2326,14 @@ function FileUpload({
2196
2326
  isRequired,
2197
2327
  fullWidth = true,
2198
2328
  className,
2199
- style
2329
+ style,
2330
+ ...rest
2200
2331
  }) {
2201
2332
  const msg = usePireMessages();
2202
- const [uncontrolled, setUncontrolled] = React13.useState(defaultValue ?? []);
2333
+ const [uncontrolled, setUncontrolled] = React17.useState(defaultValue ?? []);
2203
2334
  const files = value ?? uncontrolled;
2204
- const created = React13.useRef([]);
2205
- React13.useEffect(() => () => {
2335
+ const created = React17.useRef([]);
2336
+ React17.useEffect(() => () => {
2206
2337
  for (const url of created.current) URL.revokeObjectURL(url);
2207
2338
  }, []);
2208
2339
  const commit = (next) => {
@@ -2229,6 +2360,7 @@ function FileUpload({
2229
2360
  style,
2230
2361
  "data-full-width": String(fullWidth),
2231
2362
  "data-invalid": isInvalid ? "true" : void 0,
2363
+ ...rest,
2232
2364
  children: [
2233
2365
  label ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "pire-field-label", children: [
2234
2366
  label,
@@ -2313,7 +2445,7 @@ function ShellBar({
2313
2445
  }
2314
2446
 
2315
2447
  // src/components/navigation/SideNav.tsx
2316
- var React14 = __toESM(require("react"), 1);
2448
+ var React18 = __toESM(require("react"), 1);
2317
2449
  var import_react_aria_components26 = require("react-aria-components");
2318
2450
 
2319
2451
  // src/components/navigation/Menu.tsx
@@ -2400,10 +2532,10 @@ function SideNav({
2400
2532
  const msg = usePireMessages();
2401
2533
  const activeParent = findActiveParent(groups, value);
2402
2534
  const isControlled = expandedIds != null;
2403
- const [ownExpanded, setOwnExpanded] = React14.useState(
2535
+ const [ownExpanded, setOwnExpanded] = React18.useState(
2404
2536
  () => new Set(defaultExpandedIds ?? (activeParent ? [activeParent] : []))
2405
2537
  );
2406
- const [lastValue, setLastValue] = React14.useState(value);
2538
+ const [lastValue, setLastValue] = React18.useState(value);
2407
2539
  if (value !== lastValue) {
2408
2540
  setLastValue(value);
2409
2541
  if (!isControlled && activeParent && !ownExpanded.has(activeParent)) {
@@ -2667,7 +2799,7 @@ function InlineMessage({ kind = "info", title, action, onClose, children, classN
2667
2799
  }
2668
2800
 
2669
2801
  // src/components/feedback/Toast.tsx
2670
- var React15 = __toESM(require("react"), 1);
2802
+ var React19 = __toESM(require("react"), 1);
2671
2803
  var import_jsx_runtime42 = require("react/jsx-runtime");
2672
2804
  var KIND_ICON2 = {
2673
2805
  info: "info",
@@ -2693,26 +2825,26 @@ function Toast({ kind = "success", onClose, position = "bottom-center", children
2693
2825
  }
2694
2826
  );
2695
2827
  }
2696
- var ToastContext = React15.createContext(null);
2828
+ var ToastContext = React19.createContext(null);
2697
2829
  function ToastProvider({ children, timeout = 6e3 }) {
2698
2830
  const msg = usePireMessages();
2699
- const [toasts, setToasts] = React15.useState([]);
2700
- const close = React15.useCallback((id) => setToasts((t) => t.filter((x) => x.id !== id)), []);
2701
- const add = React15.useCallback((toast) => {
2831
+ const [toasts, setToasts] = React19.useState([]);
2832
+ const close = React19.useCallback((id) => setToasts((t) => t.filter((x) => x.id !== id)), []);
2833
+ const add = React19.useCallback((toast) => {
2702
2834
  const id = Math.random().toString(36).slice(2);
2703
2835
  setToasts((t) => [...t, { ...toast, id }]);
2704
2836
  const ms = toast.timeout ?? timeout;
2705
2837
  if (ms > 0) setTimeout(() => close(id), ms);
2706
2838
  return id;
2707
2839
  }, [close, timeout]);
2708
- const value = React15.useMemo(() => ({ add, close }), [add, close]);
2840
+ const value = React19.useMemo(() => ({ add, close }), [add, close]);
2709
2841
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(ToastContext.Provider, { value, children: [
2710
2842
  children,
2711
2843
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "pire-toast-region", role: "region", "aria-label": msg.toastRegionLabel, children: toasts.map((t) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Toast, { kind: t.kind, onClose: () => close(t.id), style: { position: "static", transform: "none" }, children: t.message }, t.id)) })
2712
2844
  ] });
2713
2845
  }
2714
2846
  function useToast() {
2715
- const ctx = React15.useContext(ToastContext);
2847
+ const ctx = React19.useContext(ToastContext);
2716
2848
  if (!ctx) throw new Error("useToast must be used inside <ToastProvider>");
2717
2849
  return ctx;
2718
2850
  }
@@ -3062,14 +3194,14 @@ function BarChart({
3062
3194
  }
3063
3195
 
3064
3196
  // src/components/data/LineChart.tsx
3065
- var React16 = __toESM(require("react"), 1);
3197
+ var React20 = __toESM(require("react"), 1);
3066
3198
  var import_jsx_runtime50 = require("react/jsx-runtime");
3067
3199
  var PLOT_INSET = { top: 10, right: 14, bottom: 24, left: 58 };
3068
3200
  var MARKER_LIMIT = 20;
3069
- var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? React16.useEffect : React16.useLayoutEffect;
3201
+ var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? React20.useEffect : React20.useLayoutEffect;
3070
3202
  function useMeasuredWidth() {
3071
- const ref = React16.useRef(null);
3072
- const [width, setWidth] = React16.useState(0);
3203
+ const ref = React20.useRef(null);
3204
+ const [width, setWidth] = React20.useState(0);
3073
3205
  useIsomorphicLayoutEffect2(() => {
3074
3206
  const element = ref.current;
3075
3207
  if (!element) return;
@@ -3232,7 +3364,7 @@ function LineChart({
3232
3364
  }
3233
3365
 
3234
3366
  // src/components/data/DonutChart.tsx
3235
- var React17 = __toESM(require("react"), 1);
3367
+ var React21 = __toESM(require("react"), 1);
3236
3368
  var import_jsx_runtime51 = require("react/jsx-runtime");
3237
3369
  var SEGMENT_GAP_DEGREES = 1.5;
3238
3370
  function DonutChart({
@@ -3253,7 +3385,7 @@ function DonutChart({
3253
3385
  }) {
3254
3386
  const label = rest["aria-label"];
3255
3387
  const msg = usePireMessages();
3256
- const [hoveredIndex, setHoveredIndex] = React17.useState(null);
3388
+ const [hoveredIndex, setHoveredIndex] = React21.useState(null);
3257
3389
  warnOnDonutShape(series, categories.length, label);
3258
3390
  const values = (series[0]?.values ?? []).slice(0, categories.length);
3259
3391
  const segments = categories.map((category, index) => ({ category, index, value: values[index] ?? 0 })).filter((segment) => segment.value > 0);
@@ -3490,7 +3622,7 @@ function ConfirmDialog({
3490
3622
  }
3491
3623
 
3492
3624
  // src/components/patterns/CommandPalette.tsx
3493
- var React18 = __toESM(require("react"), 1);
3625
+ var React22 = __toESM(require("react"), 1);
3494
3626
  var import_react_aria_components31 = require("react-aria-components");
3495
3627
  var import_jsx_runtime60 = require("react/jsx-runtime");
3496
3628
  var RESULTS_SECTION_KEY = "__pire_results__";
@@ -3513,17 +3645,17 @@ function CommandPalette({
3513
3645
  style
3514
3646
  }) {
3515
3647
  const msg = usePireMessages();
3516
- const [query, setQuery] = React18.useState("");
3648
+ const [query, setQuery] = React22.useState("");
3517
3649
  const { contains } = (0, import_react_aria_components31.useFilter)({ sensitivity: "base" });
3518
- React18.useEffect(() => {
3650
+ React22.useEffect(() => {
3519
3651
  if (isOpen) setQuery("");
3520
3652
  }, [isOpen]);
3521
- const searchableById = React18.useMemo(() => {
3653
+ const searchableById = React22.useMemo(() => {
3522
3654
  const byId = /* @__PURE__ */ new Map();
3523
3655
  for (const group of groups) for (const item of group.items) byId.set(item.id, searchableText(item));
3524
3656
  return byId;
3525
3657
  }, [groups]);
3526
- const filter = React18.useCallback((textValue, inputValue, node) => {
3658
+ const filter = React22.useCallback((textValue, inputValue, node) => {
3527
3659
  if (node.parentKey === RESULTS_SECTION_KEY) return true;
3528
3660
  return contains(searchableById.get(String(node.key)) ?? textValue, inputValue);
3529
3661
  }, [contains, searchableById]);
@@ -3606,9 +3738,9 @@ function CommandPalette({
3606
3738
  );
3607
3739
  }
3608
3740
  function useCommandPaletteShortcut(onTrigger, { key = "k", isDisabled } = {}) {
3609
- const handler = React18.useRef(onTrigger);
3741
+ const handler = React22.useRef(onTrigger);
3610
3742
  handler.current = onTrigger;
3611
- React18.useEffect(() => {
3743
+ React22.useEffect(() => {
3612
3744
  if (isDisabled) return;
3613
3745
  const onKeyDown = (event) => {
3614
3746
  if (!(event.metaKey || event.ctrlKey) || event.altKey) return;
@@ -3662,7 +3794,7 @@ function EmptyState({ icon = "file-text", title, action, compact, children, clas
3662
3794
  }
3663
3795
 
3664
3796
  // src/components/patterns/FileDropZone.tsx
3665
- var React19 = __toESM(require("react"), 1);
3797
+ var React23 = __toESM(require("react"), 1);
3666
3798
  var import_react_aria_components33 = require("react-aria-components");
3667
3799
  var import_jsx_runtime63 = require("react/jsx-runtime");
3668
3800
  var isImage2 = (file) => file.type.startsWith("image/");
@@ -3687,13 +3819,15 @@ function FileDropZone({
3687
3819
  isRequired,
3688
3820
  fullWidth = true,
3689
3821
  className,
3690
- style
3822
+ style,
3823
+ "aria-label": ariaLabel,
3824
+ ...rest
3691
3825
  }) {
3692
3826
  const msg = usePireMessages();
3693
- const [uncontrolled, setUncontrolled] = React19.useState(defaultValue ?? []);
3827
+ const [uncontrolled, setUncontrolled] = React23.useState(defaultValue ?? []);
3694
3828
  const files = value ?? uncontrolled;
3695
- const created = React19.useRef([]);
3696
- React19.useEffect(() => () => {
3829
+ const created = React23.useRef([]);
3830
+ React23.useEffect(() => () => {
3697
3831
  for (const url of created.current) URL.revokeObjectURL(url);
3698
3832
  }, []);
3699
3833
  const commit = (next) => {
@@ -3713,7 +3847,7 @@ function FileDropZone({
3713
3847
  commit(allowsMultiple ? [...files, ...wrapped] : wrapped.slice(0, 1));
3714
3848
  };
3715
3849
  const remove = (target) => commit(files.filter((f) => f.file !== target));
3716
- return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: cx("pire-field", className), style, "data-full-width": String(fullWidth), children: [
3850
+ return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("div", { className: cx("pire-field", className), style, "data-full-width": String(fullWidth), ...rest, children: [
3717
3851
  label ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)("span", { className: "pire-field-label", children: [
3718
3852
  label,
3719
3853
  isRequired ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "pire-field-req", "aria-hidden": "true", children: "*" }) : null
@@ -3722,6 +3856,7 @@ function FileDropZone({
3722
3856
  import_react_aria_components33.DropZone,
3723
3857
  {
3724
3858
  className: "pire-dropzone",
3859
+ "aria-label": ariaLabel,
3725
3860
  "data-height": height,
3726
3861
  isDisabled: isDisabled || isReadOnly,
3727
3862
  onDrop: async (e) => {