@orianatech/pire 0.23.0 → 1.0.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.
package/dist/index.js CHANGED
@@ -865,6 +865,12 @@ function useMessage(key, override) {
865
865
  return override ?? messages[key];
866
866
  }
867
867
 
868
+ // src/components/utils/focusField.ts
869
+ function focusFieldOf(button) {
870
+ const control = button?.closest?.(".pire-control");
871
+ control?.querySelector(".pire-datesegment, .pire-input, input, textarea")?.focus();
872
+ }
873
+
868
874
  // src/components/forms/Input.tsx
869
875
  import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
870
876
  function Input({
@@ -920,7 +926,17 @@ function Input({
920
926
  children: [
921
927
  icon ? /* @__PURE__ */ jsx17(Icon, { name: icon, size: 16, className: "pire-leading-icon" }) : null,
922
928
  /* @__PURE__ */ jsx17(AriaInput, { ref: inputRef, className: "pire-input", "data-numeric": numeric ? "true" : void 0 }),
923
- showsClear ? /* @__PURE__ */ jsx17(AriaButton4, { className: "pire-input-clear", onPress: () => handleChange(""), children: clearLabel ?? msg.inputClear }) : null,
929
+ showsClear ? /* @__PURE__ */ jsx17(
930
+ AriaButton4,
931
+ {
932
+ className: "pire-input-clear",
933
+ onPress: (event) => {
934
+ handleChange("");
935
+ focusFieldOf(event.target);
936
+ },
937
+ children: clearLabel ?? msg.inputClear
938
+ }
939
+ ) : null,
924
940
  suffix ? /* @__PURE__ */ jsx17("span", { className: "pire-affix", children: suffix }) : null
925
941
  ]
926
942
  }
@@ -1240,7 +1256,18 @@ function ComboBox({
1240
1256
  children: [
1241
1257
  /* @__PURE__ */ jsx23(Icon, { name: "search", size: 16, className: "pire-leading-icon" }),
1242
1258
  /* @__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,
1259
+ showsClear ? /* @__PURE__ */ jsx23(
1260
+ AriaButton6,
1261
+ {
1262
+ className: "pire-input-clear",
1263
+ slot: null,
1264
+ onPress: (event) => {
1265
+ clear();
1266
+ focusFieldOf(event.target);
1267
+ },
1268
+ children: clearLabel ?? msg.inputClear
1269
+ }
1270
+ ) : null,
1244
1271
  /* @__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 }) })
1245
1272
  ]
1246
1273
  }
@@ -1424,6 +1451,7 @@ function MultiComboBox({
1424
1451
  }
1425
1452
 
1426
1453
  // src/components/forms/NumberField.tsx
1454
+ import * as React14 from "react";
1427
1455
  import {
1428
1456
  NumberField as AriaNumberField,
1429
1457
  Label as Label7,
@@ -1432,7 +1460,8 @@ import {
1432
1460
  Button as AriaButton8,
1433
1461
  Text as Text9,
1434
1462
  FieldError as FieldError7,
1435
- useLocale
1463
+ useLocale,
1464
+ NumberFieldStateContext
1436
1465
  } from "react-aria-components";
1437
1466
  import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
1438
1467
  function decimalSeparatorFor(locale) {
@@ -1447,6 +1476,17 @@ function typeIntoInput(el, text) {
1447
1476
  const caret = start + text.length;
1448
1477
  el.setSelectionRange(caret, caret);
1449
1478
  }
1479
+ var SINGLE_DOT = /^(-?\d+)\.(\d+)$/;
1480
+ function couldBeGrouped(integerPart, fractionPart) {
1481
+ return fractionPart.length === 3 && integerPart.replace("-", "").length <= 3;
1482
+ }
1483
+ function localiseMachineDecimal(text, separator) {
1484
+ const match = SINGLE_DOT.exec(text.trim());
1485
+ if (!match || couldBeGrouped(match[1], match[2])) {
1486
+ return text;
1487
+ }
1488
+ return `${match[1]}${separator}${match[2]}`;
1489
+ }
1450
1490
  function toAriaNumber(value) {
1451
1491
  if (value === void 0) {
1452
1492
  return void 0;
@@ -1456,6 +1496,24 @@ function toAriaNumber(value) {
1456
1496
  }
1457
1497
  return typeof value === "number" ? value : Number(value);
1458
1498
  }
1499
+ function ClearButton({ label, onClear }) {
1500
+ const state = React14.useContext(NumberFieldStateContext);
1501
+ if (!state?.inputValue) {
1502
+ return null;
1503
+ }
1504
+ return /* @__PURE__ */ jsx25(
1505
+ AriaButton8,
1506
+ {
1507
+ className: "pire-input-clear",
1508
+ slot: null,
1509
+ onPress: (event) => {
1510
+ onClear();
1511
+ focusFieldOf(event.target);
1512
+ },
1513
+ children: label
1514
+ }
1515
+ );
1516
+ }
1459
1517
  function NumberField({
1460
1518
  label,
1461
1519
  suffix,
@@ -1470,12 +1528,24 @@ function NumberField({
1470
1528
  defaultValue,
1471
1529
  onChange,
1472
1530
  emptyValue,
1531
+ isClearable,
1532
+ clearLabel,
1473
1533
  ...rest
1474
1534
  }) {
1475
1535
  const msg = usePireMessages();
1476
1536
  const { locale } = useLocale();
1477
1537
  const empty = emptyValue === void 0 ? NaN : emptyValue;
1478
- const handleChange = onChange && ((next) => onChange(Number.isNaN(next) ? empty : next));
1538
+ const isControlled = value !== void 0;
1539
+ const [ownValue, setOwnValue] = React14.useState(() => toAriaNumber(defaultValue) ?? NaN);
1540
+ const takesOverValue = Boolean(isClearable) && !isControlled;
1541
+ const valueProps = takesOverValue ? { value: ownValue, defaultValue: void 0 } : { value: toAriaNumber(value), defaultValue: toAriaNumber(defaultValue) };
1542
+ const handleChange = (next) => {
1543
+ if (takesOverValue) {
1544
+ setOwnValue(next);
1545
+ }
1546
+ onChange?.(Number.isNaN(next) ? empty : next);
1547
+ };
1548
+ const showsClear = Boolean(isClearable) && !rest.isDisabled && !rest.isReadOnly;
1479
1549
  const handleDecimalKey = (event) => {
1480
1550
  if (event.key !== "." || event.ctrlKey || event.metaKey || event.altKey) {
1481
1551
  return;
@@ -1494,16 +1564,36 @@ function NumberField({
1494
1564
  event.preventDefault();
1495
1565
  typeIntoInput(event.currentTarget, separator);
1496
1566
  };
1567
+ const handlePaste = (event) => {
1568
+ if (rest.isReadOnly || rest.isDisabled) {
1569
+ return;
1570
+ }
1571
+ const separator = decimalSeparatorFor(locale);
1572
+ if (separator === ".") {
1573
+ return;
1574
+ }
1575
+ const { maximumFractionDigits } = new Intl.NumberFormat(locale, rest.formatOptions).resolvedOptions();
1576
+ if (!maximumFractionDigits) {
1577
+ return;
1578
+ }
1579
+ const pasted = event.clipboardData.getData("text");
1580
+ const localised = localiseMachineDecimal(pasted, separator);
1581
+ if (localised === pasted) {
1582
+ return;
1583
+ }
1584
+ event.preventDefault();
1585
+ event.stopPropagation();
1586
+ typeIntoInput(event.currentTarget, localised);
1587
+ };
1497
1588
  return /* @__PURE__ */ jsxs18(
1498
1589
  AriaNumberField,
1499
1590
  {
1500
1591
  className: cx("pire-field", className),
1501
1592
  style,
1502
1593
  validationBehavior: "aria",
1503
- value: toAriaNumber(value),
1504
- defaultValue: toAriaNumber(defaultValue),
1505
1594
  onChange: handleChange,
1506
1595
  ...rest,
1596
+ ...valueProps,
1507
1597
  children: [
1508
1598
  label ? /* @__PURE__ */ jsxs18(Label7, { className: "pire-field-label", children: [
1509
1599
  label,
@@ -1518,8 +1608,17 @@ function NumberField({
1518
1608
  "data-invalid": rest.isInvalid ? "true" : void 0,
1519
1609
  "data-readonly": rest.isReadOnly ? "true" : void 0,
1520
1610
  children: [
1521
- /* @__PURE__ */ jsx25(AriaInput4, { className: "pire-input", "data-numeric": "true", onKeyDown: handleDecimalKey }),
1611
+ /* @__PURE__ */ jsx25(
1612
+ AriaInput4,
1613
+ {
1614
+ className: "pire-input",
1615
+ "data-numeric": "true",
1616
+ onKeyDown: handleDecimalKey,
1617
+ onPasteCapture: handlePaste
1618
+ }
1619
+ ),
1522
1620
  suffix ? /* @__PURE__ */ jsx25("span", { className: "pire-affix", children: suffix }) : null,
1621
+ showsClear ? /* @__PURE__ */ jsx25(ClearButton, { label: clearLabel ?? msg.inputClear, onClear: () => handleChange(NaN) }) : null,
1523
1622
  hideStepper ? null : /* @__PURE__ */ jsxs18("span", { className: "pire-stepper", children: [
1524
1623
  /* @__PURE__ */ jsx25(AriaButton8, { slot: "increment", className: "pire-stepper-btn", "aria-label": msg.numberFieldIncrease, children: /* @__PURE__ */ jsx25(Icon, { name: "plus", size: 10 }) }),
1525
1624
  /* @__PURE__ */ jsx25(AriaButton8, { slot: "decrement", className: "pire-stepper-btn", "aria-label": msg.numberFieldDecrease, children: /* @__PURE__ */ jsx25(Icon, { name: "minus", size: 10 }) })
@@ -1535,6 +1634,7 @@ function NumberField({
1535
1634
  }
1536
1635
 
1537
1636
  // src/components/forms/DatePicker.tsx
1637
+ import * as React15 from "react";
1538
1638
  import {
1539
1639
  DatePicker as AriaDatePicker,
1540
1640
  Label as Label8,
@@ -1552,7 +1652,8 @@ import {
1552
1652
  CalendarCell,
1553
1653
  Heading as Heading2,
1554
1654
  Text as Text10,
1555
- FieldError as FieldError8
1655
+ FieldError as FieldError8,
1656
+ DatePickerStateContext
1556
1657
  } from "react-aria-components";
1557
1658
 
1558
1659
  // src/components/forms/dateValues.ts
@@ -1584,6 +1685,24 @@ function coerceBound(value) {
1584
1685
 
1585
1686
  // src/components/forms/DatePicker.tsx
1586
1687
  import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
1688
+ function ClearButton2({ label }) {
1689
+ const state = React15.useContext(DatePickerStateContext);
1690
+ if (!state?.value) {
1691
+ return null;
1692
+ }
1693
+ return /* @__PURE__ */ jsx26(
1694
+ AriaButton9,
1695
+ {
1696
+ className: "pire-input-clear",
1697
+ slot: null,
1698
+ onPress: (event) => {
1699
+ state.setValue(null);
1700
+ focusFieldOf(event.target);
1701
+ },
1702
+ children: label
1703
+ }
1704
+ );
1705
+ }
1587
1706
  function DatePicker({
1588
1707
  label,
1589
1708
  description,
@@ -1598,22 +1717,25 @@ function DatePicker({
1598
1717
  valueFormat,
1599
1718
  minValue,
1600
1719
  maxValue,
1720
+ isClearable,
1721
+ clearLabel,
1601
1722
  ...rest
1602
1723
  }) {
1603
1724
  const msg = usePireMessages();
1604
- const handleChange = onChange && ((next) => onChange(
1725
+ const showsClear = Boolean(isClearable) && !rest.isDisabled && !rest.isReadOnly;
1726
+ const handleChange = (next) => onChange?.(
1605
1727
  valueFormat === "iso" && next ? next.toString() : next
1606
- ));
1728
+ );
1607
1729
  return /* @__PURE__ */ jsxs19(
1608
1730
  AriaDatePicker,
1609
1731
  {
1610
1732
  className: cx("pire-field", className),
1611
1733
  style,
1612
1734
  validationBehavior: "aria",
1613
- value: coerceDateValue(value),
1614
- defaultValue: coerceDateValue(defaultValue),
1615
1735
  minValue: coerceBound(minValue),
1616
1736
  maxValue: coerceBound(maxValue),
1737
+ value: coerceDateValue(value),
1738
+ defaultValue: coerceDateValue(defaultValue),
1617
1739
  onChange: handleChange,
1618
1740
  ...rest,
1619
1741
  children: [
@@ -1631,6 +1753,7 @@ function DatePicker({
1631
1753
  "data-readonly": rest.isReadOnly ? "true" : void 0,
1632
1754
  children: [
1633
1755
  /* @__PURE__ */ jsx26(DateInput, { className: "pire-datefield", children: (segment) => /* @__PURE__ */ jsx26(DateSegment, { segment, className: "pire-datesegment" }) }),
1756
+ showsClear ? /* @__PURE__ */ jsx26(ClearButton2, { label: clearLabel ?? msg.inputClear }) : null,
1634
1757
  /* @__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 }) })
1635
1758
  ]
1636
1759
  }
@@ -1654,6 +1777,7 @@ function DatePicker({
1654
1777
  }
1655
1778
 
1656
1779
  // src/components/forms/DateRangePicker.tsx
1780
+ import * as React16 from "react";
1657
1781
  import {
1658
1782
  DateRangePicker as AriaDateRangePicker,
1659
1783
  Label as Label9,
@@ -1671,7 +1795,8 @@ import {
1671
1795
  CalendarCell as CalendarCell2,
1672
1796
  Heading as Heading3,
1673
1797
  Text as Text11,
1674
- FieldError as FieldError9
1798
+ FieldError as FieldError9,
1799
+ DateRangePickerStateContext
1675
1800
  } from "react-aria-components";
1676
1801
  import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
1677
1802
  function coerceRange(range) {
@@ -1693,6 +1818,24 @@ function rangeLengthInDays(range) {
1693
1818
  }
1694
1819
  return end.toDate("UTC").getTime() / 864e5 - start.toDate("UTC").getTime() / 864e5 + 1;
1695
1820
  }
1821
+ function ClearButton3({ label }) {
1822
+ const state = React16.useContext(DateRangePickerStateContext);
1823
+ if (!state?.value?.start && !state?.value?.end) {
1824
+ return null;
1825
+ }
1826
+ return /* @__PURE__ */ jsx27(
1827
+ AriaButton10,
1828
+ {
1829
+ className: "pire-input-clear",
1830
+ slot: null,
1831
+ onPress: (event) => {
1832
+ state.setValue(null);
1833
+ focusFieldOf(event.target);
1834
+ },
1835
+ children: label
1836
+ }
1837
+ );
1838
+ }
1696
1839
  function DateRangePicker({
1697
1840
  label,
1698
1841
  description,
@@ -1711,6 +1854,8 @@ function DateRangePicker({
1711
1854
  valueFormat,
1712
1855
  minValue,
1713
1856
  maxValue,
1857
+ isClearable,
1858
+ clearLabel,
1714
1859
  ...rest
1715
1860
  }) {
1716
1861
  const msg = usePireMessages();
@@ -1739,7 +1884,7 @@ function DateRangePicker({
1739
1884
  defaultValue: coerceRange(defaultValue),
1740
1885
  minValue: coerceBound(minValue),
1741
1886
  maxValue: coerceBound(maxValue),
1742
- onChange: onChange && emit,
1887
+ onChange: emit,
1743
1888
  ...rest,
1744
1889
  isInvalid: rest.isInvalid || tooLong || void 0,
1745
1890
  children: [
@@ -1759,6 +1904,7 @@ function DateRangePicker({
1759
1904
  /* @__PURE__ */ jsx27(DateInput2, { slot: "start", className: "pire-datefield", children: (segment) => /* @__PURE__ */ jsx27(DateSegment2, { segment, className: "pire-datesegment" }) }),
1760
1905
  /* @__PURE__ */ jsx27("span", { "aria-hidden": "true", className: "pire-daterange-dash", children: "\u2013" }),
1761
1906
  /* @__PURE__ */ jsx27(DateInput2, { slot: "end", className: "pire-datefield", children: (segment) => /* @__PURE__ */ jsx27(DateSegment2, { segment, className: "pire-datesegment" }) }),
1907
+ isClearable && !rest.isDisabled && !rest.isReadOnly ? /* @__PURE__ */ jsx27(ClearButton3, { label: clearLabel ?? msg.inputClear }) : null,
1762
1908
  /* @__PURE__ */ jsx27(
1763
1909
  AriaButton10,
1764
1910
  {
@@ -1824,13 +1970,13 @@ function DateRangePicker({
1824
1970
  }
1825
1971
 
1826
1972
  // src/components/forms/ValueHelpField.tsx
1827
- import * as React16 from "react";
1973
+ import * as React19 from "react";
1828
1974
 
1829
1975
  // src/components/patterns/ValueHelpDialog.tsx
1830
- import * as React15 from "react";
1976
+ import * as React18 from "react";
1831
1977
 
1832
1978
  // src/components/feedback/Dialog.tsx
1833
- import * as React14 from "react";
1979
+ import * as React17 from "react";
1834
1980
  import { ModalOverlay, Modal, Dialog as AriaDialog3, Heading as Heading4 } from "react-aria-components";
1835
1981
  import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
1836
1982
  function DialogTitle({ children, className }) {
@@ -1864,8 +2010,8 @@ function Dialog({
1864
2010
  onClose?.();
1865
2011
  onOpenChange?.(false);
1866
2012
  };
1867
- const parts = React14.Children.toArray(children);
1868
- const slotOf = (type) => parts.find((node) => React14.isValidElement(node) && node.type === type);
2013
+ const parts = React17.Children.toArray(children);
2014
+ const slotOf = (type) => parts.find((node) => React17.isValidElement(node) && node.type === type);
1869
2015
  const titleSlot = slotOf(DialogTitle);
1870
2016
  const descriptionSlot = slotOf(DialogDescription);
1871
2017
  const bodySlot = slotOf(DialogBody);
@@ -2122,11 +2268,11 @@ function ValueHelpDialog({
2122
2268
  onClose
2123
2269
  }) {
2124
2270
  const msg = usePireMessages();
2125
- const [query, setQuery] = React15.useState("");
2126
- React15.useEffect(() => {
2271
+ const [query, setQuery] = React18.useState("");
2272
+ React18.useEffect(() => {
2127
2273
  if (isOpen) setQuery("");
2128
2274
  }, [isOpen]);
2129
- const filtered = React15.useMemo(() => {
2275
+ const filtered = React18.useMemo(() => {
2130
2276
  if (isFilteredExternally) return rows;
2131
2277
  const q = query.trim().toLowerCase();
2132
2278
  if (!q) return rows;
@@ -2208,7 +2354,7 @@ function ValueHelpField({
2208
2354
  style
2209
2355
  }) {
2210
2356
  const msg = usePireMessages();
2211
- const [open, setOpen] = React16.useState(false);
2357
+ const [open, setOpen] = React19.useState(false);
2212
2358
  return /* @__PURE__ */ jsxs24("div", { className: cx(className), style, children: [
2213
2359
  /* @__PURE__ */ jsx32(
2214
2360
  Input,
@@ -2264,7 +2410,7 @@ function ValueHelpField({
2264
2410
  }
2265
2411
 
2266
2412
  // src/components/forms/FileUpload.tsx
2267
- import * as React17 from "react";
2413
+ import * as React20 from "react";
2268
2414
  import { FileTrigger, Text as Text12 } from "react-aria-components";
2269
2415
 
2270
2416
  // src/components/feedback/ProgressBar.tsx
@@ -2355,10 +2501,10 @@ function FileUpload({
2355
2501
  ...rest
2356
2502
  }) {
2357
2503
  const msg = usePireMessages();
2358
- const [uncontrolled, setUncontrolled] = React17.useState(defaultValue ?? []);
2504
+ const [uncontrolled, setUncontrolled] = React20.useState(defaultValue ?? []);
2359
2505
  const files = value ?? uncontrolled;
2360
- const created = React17.useRef([]);
2361
- React17.useEffect(() => () => {
2506
+ const created = React20.useRef([]);
2507
+ React20.useEffect(() => () => {
2362
2508
  for (const url of created.current) URL.revokeObjectURL(url);
2363
2509
  }, []);
2364
2510
  const commit = (next) => {
@@ -2470,7 +2616,7 @@ function ShellBar({
2470
2616
  }
2471
2617
 
2472
2618
  // src/components/navigation/SideNav.tsx
2473
- import * as React18 from "react";
2619
+ import * as React21 from "react";
2474
2620
  import {
2475
2621
  Button as AriaButton12,
2476
2622
  Disclosure,
@@ -2570,10 +2716,10 @@ function SideNav({
2570
2716
  const msg = usePireMessages();
2571
2717
  const activeParent = findActiveParent(groups, value);
2572
2718
  const isControlled = expandedIds != null;
2573
- const [ownExpanded, setOwnExpanded] = React18.useState(
2719
+ const [ownExpanded, setOwnExpanded] = React21.useState(
2574
2720
  () => new Set(defaultExpandedIds ?? (activeParent ? [activeParent] : []))
2575
2721
  );
2576
- const [lastValue, setLastValue] = React18.useState(value);
2722
+ const [lastValue, setLastValue] = React21.useState(value);
2577
2723
  if (value !== lastValue) {
2578
2724
  setLastValue(value);
2579
2725
  if (!isControlled && activeParent && !ownExpanded.has(activeParent)) {
@@ -2837,7 +2983,7 @@ function InlineMessage({ kind = "info", title, action, onClose, children, classN
2837
2983
  }
2838
2984
 
2839
2985
  // src/components/feedback/Toast.tsx
2840
- import * as React19 from "react";
2986
+ import * as React22 from "react";
2841
2987
  import { jsx as jsx42, jsxs as jsxs34 } from "react/jsx-runtime";
2842
2988
  var KIND_ICON2 = {
2843
2989
  info: "info",
@@ -2863,26 +3009,26 @@ function Toast({ kind = "success", onClose, position = "bottom-center", children
2863
3009
  }
2864
3010
  );
2865
3011
  }
2866
- var ToastContext = React19.createContext(null);
3012
+ var ToastContext = React22.createContext(null);
2867
3013
  function ToastProvider({ children, timeout = 6e3 }) {
2868
3014
  const msg = usePireMessages();
2869
- const [toasts, setToasts] = React19.useState([]);
2870
- const close = React19.useCallback((id) => setToasts((t) => t.filter((x) => x.id !== id)), []);
2871
- const add = React19.useCallback((toast) => {
3015
+ const [toasts, setToasts] = React22.useState([]);
3016
+ const close = React22.useCallback((id) => setToasts((t) => t.filter((x) => x.id !== id)), []);
3017
+ const add = React22.useCallback((toast) => {
2872
3018
  const id = Math.random().toString(36).slice(2);
2873
3019
  setToasts((t) => [...t, { ...toast, id }]);
2874
3020
  const ms = toast.timeout ?? timeout;
2875
3021
  if (ms > 0) setTimeout(() => close(id), ms);
2876
3022
  return id;
2877
3023
  }, [close, timeout]);
2878
- const value = React19.useMemo(() => ({ add, close }), [add, close]);
3024
+ const value = React22.useMemo(() => ({ add, close }), [add, close]);
2879
3025
  return /* @__PURE__ */ jsxs34(ToastContext.Provider, { value, children: [
2880
3026
  children,
2881
3027
  /* @__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)) })
2882
3028
  ] });
2883
3029
  }
2884
3030
  function useToast() {
2885
- const ctx = React19.useContext(ToastContext);
3031
+ const ctx = React22.useContext(ToastContext);
2886
3032
  if (!ctx) throw new Error("useToast must be used inside <ToastProvider>");
2887
3033
  return ctx;
2888
3034
  }
@@ -3232,14 +3378,14 @@ function BarChart({
3232
3378
  }
3233
3379
 
3234
3380
  // src/components/data/LineChart.tsx
3235
- import * as React20 from "react";
3381
+ import * as React23 from "react";
3236
3382
  import { jsx as jsx50, jsxs as jsxs42 } from "react/jsx-runtime";
3237
3383
  var PLOT_INSET = { top: 10, right: 14, bottom: 24, left: 58 };
3238
3384
  var MARKER_LIMIT = 20;
3239
- var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? React20.useEffect : React20.useLayoutEffect;
3385
+ var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? React23.useEffect : React23.useLayoutEffect;
3240
3386
  function useMeasuredWidth() {
3241
- const ref = React20.useRef(null);
3242
- const [width, setWidth] = React20.useState(0);
3387
+ const ref = React23.useRef(null);
3388
+ const [width, setWidth] = React23.useState(0);
3243
3389
  useIsomorphicLayoutEffect2(() => {
3244
3390
  const element = ref.current;
3245
3391
  if (!element) return;
@@ -3402,7 +3548,7 @@ function LineChart({
3402
3548
  }
3403
3549
 
3404
3550
  // src/components/data/DonutChart.tsx
3405
- import * as React21 from "react";
3551
+ import * as React24 from "react";
3406
3552
  import { jsx as jsx51, jsxs as jsxs43 } from "react/jsx-runtime";
3407
3553
  var SEGMENT_GAP_DEGREES = 1.5;
3408
3554
  function DonutChart({
@@ -3423,7 +3569,7 @@ function DonutChart({
3423
3569
  }) {
3424
3570
  const label = rest["aria-label"];
3425
3571
  const msg = usePireMessages();
3426
- const [hoveredIndex, setHoveredIndex] = React21.useState(null);
3572
+ const [hoveredIndex, setHoveredIndex] = React24.useState(null);
3427
3573
  warnOnDonutShape(series, categories.length, label);
3428
3574
  const values = (series[0]?.values ?? []).slice(0, categories.length);
3429
3575
  const segments = categories.map((category, index) => ({ category, index, value: values[index] ?? 0 })).filter((segment) => segment.value > 0);
@@ -3660,7 +3806,7 @@ function ConfirmDialog({
3660
3806
  }
3661
3807
 
3662
3808
  // src/components/patterns/CommandPalette.tsx
3663
- import * as React22 from "react";
3809
+ import * as React25 from "react";
3664
3810
  import {
3665
3811
  Autocomplete,
3666
3812
  ModalOverlay as ModalOverlay2,
@@ -3694,17 +3840,17 @@ function CommandPalette({
3694
3840
  style
3695
3841
  }) {
3696
3842
  const msg = usePireMessages();
3697
- const [query, setQuery] = React22.useState("");
3843
+ const [query, setQuery] = React25.useState("");
3698
3844
  const { contains } = useFilter({ sensitivity: "base" });
3699
- React22.useEffect(() => {
3845
+ React25.useEffect(() => {
3700
3846
  if (isOpen) setQuery("");
3701
3847
  }, [isOpen]);
3702
- const searchableById = React22.useMemo(() => {
3848
+ const searchableById = React25.useMemo(() => {
3703
3849
  const byId = /* @__PURE__ */ new Map();
3704
3850
  for (const group of groups) for (const item of group.items) byId.set(item.id, searchableText(item));
3705
3851
  return byId;
3706
3852
  }, [groups]);
3707
- const filter = React22.useCallback((textValue, inputValue, node) => {
3853
+ const filter = React25.useCallback((textValue, inputValue, node) => {
3708
3854
  if (node.parentKey === RESULTS_SECTION_KEY) return true;
3709
3855
  return contains(searchableById.get(String(node.key)) ?? textValue, inputValue);
3710
3856
  }, [contains, searchableById]);
@@ -3787,9 +3933,9 @@ function CommandPalette({
3787
3933
  );
3788
3934
  }
3789
3935
  function useCommandPaletteShortcut(onTrigger, { key = "k", isDisabled } = {}) {
3790
- const handler = React22.useRef(onTrigger);
3936
+ const handler = React25.useRef(onTrigger);
3791
3937
  handler.current = onTrigger;
3792
- React22.useEffect(() => {
3938
+ React25.useEffect(() => {
3793
3939
  if (isDisabled) return;
3794
3940
  const onKeyDown = (event) => {
3795
3941
  if (!(event.metaKey || event.ctrlKey) || event.altKey) return;
@@ -3843,7 +3989,7 @@ function EmptyState({ icon = "file-text", title, action, compact, children, clas
3843
3989
  }
3844
3990
 
3845
3991
  // src/components/patterns/FileDropZone.tsx
3846
- import * as React23 from "react";
3992
+ import * as React26 from "react";
3847
3993
  import { DropZone, FileTrigger as FileTrigger2, isFileDropItem } from "react-aria-components";
3848
3994
  import { jsx as jsx63, jsxs as jsxs54 } from "react/jsx-runtime";
3849
3995
  var isImage2 = (file) => file.type.startsWith("image/");
@@ -3873,10 +4019,10 @@ function FileDropZone({
3873
4019
  ...rest
3874
4020
  }) {
3875
4021
  const msg = usePireMessages();
3876
- const [uncontrolled, setUncontrolled] = React23.useState(defaultValue ?? []);
4022
+ const [uncontrolled, setUncontrolled] = React26.useState(defaultValue ?? []);
3877
4023
  const files = value ?? uncontrolled;
3878
- const created = React23.useRef([]);
3879
- React23.useEffect(() => () => {
4024
+ const created = React26.useRef([]);
4025
+ React26.useEffect(() => () => {
3880
4026
  for (const url of created.current) URL.revokeObjectURL(url);
3881
4027
  }, []);
3882
4028
  const commit = (next) => {