@orianatech/pire 0.22.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.cjs CHANGED
@@ -982,6 +982,12 @@ function useMessage(key, override) {
982
982
  return override ?? messages[key];
983
983
  }
984
984
 
985
+ // src/components/utils/focusField.ts
986
+ function focusFieldOf(button) {
987
+ const control = button?.closest?.(".pire-control");
988
+ control?.querySelector(".pire-datesegment, .pire-input, input, textarea")?.focus();
989
+ }
990
+
985
991
  // src/components/forms/Input.tsx
986
992
  var import_jsx_runtime17 = require("react/jsx-runtime");
987
993
  function Input({
@@ -1037,7 +1043,17 @@ function Input({
1037
1043
  children: [
1038
1044
  icon ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: icon, size: 16, className: "pire-leading-icon" }) : null,
1039
1045
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_aria_components9.Input, { ref: inputRef, className: "pire-input", "data-numeric": numeric ? "true" : void 0 }),
1040
- showsClear ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_aria_components9.Button, { className: "pire-input-clear", onPress: () => handleChange(""), children: clearLabel ?? msg.inputClear }) : null,
1046
+ showsClear ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1047
+ import_react_aria_components9.Button,
1048
+ {
1049
+ className: "pire-input-clear",
1050
+ onPress: (event) => {
1051
+ handleChange("");
1052
+ focusFieldOf(event.target);
1053
+ },
1054
+ children: clearLabel ?? msg.inputClear
1055
+ }
1056
+ ) : null,
1041
1057
  suffix ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "pire-affix", children: suffix }) : null
1042
1058
  ]
1043
1059
  }
@@ -1330,7 +1346,18 @@ function ComboBox({
1330
1346
  children: [
1331
1347
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Icon, { name: "search", size: 16, className: "pire-leading-icon" }),
1332
1348
  /* @__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,
1349
+ showsClear ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1350
+ import_react_aria_components15.Button,
1351
+ {
1352
+ className: "pire-input-clear",
1353
+ slot: null,
1354
+ onPress: (event) => {
1355
+ clear();
1356
+ focusFieldOf(event.target);
1357
+ },
1358
+ children: clearLabel ?? msg.inputClear
1359
+ }
1360
+ ) : null,
1334
1361
  /* @__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 }) })
1335
1362
  ]
1336
1363
  }
@@ -1501,6 +1528,7 @@ function MultiComboBox({
1501
1528
  }
1502
1529
 
1503
1530
  // src/components/forms/NumberField.tsx
1531
+ var React14 = __toESM(require("react"), 1);
1504
1532
  var import_react_aria_components17 = require("react-aria-components");
1505
1533
  var import_jsx_runtime25 = require("react/jsx-runtime");
1506
1534
  function decimalSeparatorFor(locale) {
@@ -1515,6 +1543,17 @@ function typeIntoInput(el, text) {
1515
1543
  const caret = start + text.length;
1516
1544
  el.setSelectionRange(caret, caret);
1517
1545
  }
1546
+ var SINGLE_DOT = /^(-?\d+)\.(\d+)$/;
1547
+ function couldBeGrouped(integerPart, fractionPart) {
1548
+ return fractionPart.length === 3 && integerPart.replace("-", "").length <= 3;
1549
+ }
1550
+ function localiseMachineDecimal(text, separator) {
1551
+ const match = SINGLE_DOT.exec(text.trim());
1552
+ if (!match || couldBeGrouped(match[1], match[2])) {
1553
+ return text;
1554
+ }
1555
+ return `${match[1]}${separator}${match[2]}`;
1556
+ }
1518
1557
  function toAriaNumber(value) {
1519
1558
  if (value === void 0) {
1520
1559
  return void 0;
@@ -1524,6 +1563,24 @@ function toAriaNumber(value) {
1524
1563
  }
1525
1564
  return typeof value === "number" ? value : Number(value);
1526
1565
  }
1566
+ function ClearButton({ label, onClear }) {
1567
+ const state = React14.useContext(import_react_aria_components17.NumberFieldStateContext);
1568
+ if (!state?.inputValue) {
1569
+ return null;
1570
+ }
1571
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1572
+ import_react_aria_components17.Button,
1573
+ {
1574
+ className: "pire-input-clear",
1575
+ slot: null,
1576
+ onPress: (event) => {
1577
+ onClear();
1578
+ focusFieldOf(event.target);
1579
+ },
1580
+ children: label
1581
+ }
1582
+ );
1583
+ }
1527
1584
  function NumberField({
1528
1585
  label,
1529
1586
  suffix,
@@ -1538,12 +1595,24 @@ function NumberField({
1538
1595
  defaultValue,
1539
1596
  onChange,
1540
1597
  emptyValue,
1598
+ isClearable,
1599
+ clearLabel,
1541
1600
  ...rest
1542
1601
  }) {
1543
1602
  const msg = usePireMessages();
1544
1603
  const { locale } = (0, import_react_aria_components17.useLocale)();
1545
1604
  const empty = emptyValue === void 0 ? NaN : emptyValue;
1546
- const handleChange = onChange && ((next) => onChange(Number.isNaN(next) ? empty : next));
1605
+ const isControlled = value !== void 0;
1606
+ const [ownValue, setOwnValue] = React14.useState(() => toAriaNumber(defaultValue) ?? NaN);
1607
+ const takesOverValue = Boolean(isClearable) && !isControlled;
1608
+ const valueProps = takesOverValue ? { value: ownValue, defaultValue: void 0 } : { value: toAriaNumber(value), defaultValue: toAriaNumber(defaultValue) };
1609
+ const handleChange = (next) => {
1610
+ if (takesOverValue) {
1611
+ setOwnValue(next);
1612
+ }
1613
+ onChange?.(Number.isNaN(next) ? empty : next);
1614
+ };
1615
+ const showsClear = Boolean(isClearable) && !rest.isDisabled && !rest.isReadOnly;
1547
1616
  const handleDecimalKey = (event) => {
1548
1617
  if (event.key !== "." || event.ctrlKey || event.metaKey || event.altKey) {
1549
1618
  return;
@@ -1562,16 +1631,36 @@ function NumberField({
1562
1631
  event.preventDefault();
1563
1632
  typeIntoInput(event.currentTarget, separator);
1564
1633
  };
1634
+ const handlePaste = (event) => {
1635
+ if (rest.isReadOnly || rest.isDisabled) {
1636
+ return;
1637
+ }
1638
+ const separator = decimalSeparatorFor(locale);
1639
+ if (separator === ".") {
1640
+ return;
1641
+ }
1642
+ const { maximumFractionDigits } = new Intl.NumberFormat(locale, rest.formatOptions).resolvedOptions();
1643
+ if (!maximumFractionDigits) {
1644
+ return;
1645
+ }
1646
+ const pasted = event.clipboardData.getData("text");
1647
+ const localised = localiseMachineDecimal(pasted, separator);
1648
+ if (localised === pasted) {
1649
+ return;
1650
+ }
1651
+ event.preventDefault();
1652
+ event.stopPropagation();
1653
+ typeIntoInput(event.currentTarget, localised);
1654
+ };
1565
1655
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1566
1656
  import_react_aria_components17.NumberField,
1567
1657
  {
1568
1658
  className: cx("pire-field", className),
1569
1659
  style,
1570
1660
  validationBehavior: "aria",
1571
- value: toAriaNumber(value),
1572
- defaultValue: toAriaNumber(defaultValue),
1573
1661
  onChange: handleChange,
1574
1662
  ...rest,
1663
+ ...valueProps,
1575
1664
  children: [
1576
1665
  label ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_aria_components17.Label, { className: "pire-field-label", children: [
1577
1666
  label,
@@ -1586,8 +1675,17 @@ function NumberField({
1586
1675
  "data-invalid": rest.isInvalid ? "true" : void 0,
1587
1676
  "data-readonly": rest.isReadOnly ? "true" : void 0,
1588
1677
  children: [
1589
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_aria_components17.Input, { className: "pire-input", "data-numeric": "true", onKeyDown: handleDecimalKey }),
1678
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1679
+ import_react_aria_components17.Input,
1680
+ {
1681
+ className: "pire-input",
1682
+ "data-numeric": "true",
1683
+ onKeyDown: handleDecimalKey,
1684
+ onPasteCapture: handlePaste
1685
+ }
1686
+ ),
1590
1687
  suffix ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "pire-affix", children: suffix }) : null,
1688
+ showsClear ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ClearButton, { label: clearLabel ?? msg.inputClear, onClear: () => handleChange(NaN) }) : null,
1591
1689
  hideStepper ? null : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "pire-stepper", children: [
1592
1690
  /* @__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
1691
  /* @__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 }) })
@@ -1603,9 +1701,11 @@ function NumberField({
1603
1701
  }
1604
1702
 
1605
1703
  // src/components/forms/DatePicker.tsx
1704
+ var React15 = __toESM(require("react"), 1);
1606
1705
  var import_react_aria_components18 = require("react-aria-components");
1706
+
1707
+ // src/components/forms/dateValues.ts
1607
1708
  var import_date = require("@internationalized/date");
1608
- var import_jsx_runtime26 = require("react/jsx-runtime");
1609
1709
  var ISO_DATE = /^(\d{4}-\d{2}-\d{2})(?:[T ].*)?$/;
1610
1710
  function coerceDateValue(value) {
1611
1711
  if (value === void 0) {
@@ -1627,6 +1727,30 @@ function coerceDateValue(value) {
1627
1727
  return null;
1628
1728
  }
1629
1729
  }
1730
+ function coerceBound(value) {
1731
+ return coerceDateValue(value) ?? void 0;
1732
+ }
1733
+
1734
+ // src/components/forms/DatePicker.tsx
1735
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1736
+ function ClearButton2({ label }) {
1737
+ const state = React15.useContext(import_react_aria_components18.DatePickerStateContext);
1738
+ if (!state?.value) {
1739
+ return null;
1740
+ }
1741
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1742
+ import_react_aria_components18.Button,
1743
+ {
1744
+ className: "pire-input-clear",
1745
+ slot: null,
1746
+ onPress: (event) => {
1747
+ state.setValue(null);
1748
+ focusFieldOf(event.target);
1749
+ },
1750
+ children: label
1751
+ }
1752
+ );
1753
+ }
1630
1754
  function DatePicker({
1631
1755
  label,
1632
1756
  description,
@@ -1639,18 +1763,25 @@ function DatePicker({
1639
1763
  defaultValue,
1640
1764
  onChange,
1641
1765
  valueFormat,
1766
+ minValue,
1767
+ maxValue,
1768
+ isClearable,
1769
+ clearLabel,
1642
1770
  ...rest
1643
1771
  }) {
1644
1772
  const msg = usePireMessages();
1645
- const handleChange = onChange && ((next) => onChange(
1773
+ const showsClear = Boolean(isClearable) && !rest.isDisabled && !rest.isReadOnly;
1774
+ const handleChange = (next) => onChange?.(
1646
1775
  valueFormat === "iso" && next ? next.toString() : next
1647
- ));
1776
+ );
1648
1777
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1649
1778
  import_react_aria_components18.DatePicker,
1650
1779
  {
1651
1780
  className: cx("pire-field", className),
1652
1781
  style,
1653
1782
  validationBehavior: "aria",
1783
+ minValue: coerceBound(minValue),
1784
+ maxValue: coerceBound(maxValue),
1654
1785
  value: coerceDateValue(value),
1655
1786
  defaultValue: coerceDateValue(defaultValue),
1656
1787
  onChange: handleChange,
@@ -1670,6 +1801,7 @@ function DatePicker({
1670
1801
  "data-readonly": rest.isReadOnly ? "true" : void 0,
1671
1802
  children: [
1672
1803
  /* @__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" }) }),
1804
+ showsClear ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ClearButton2, { label: clearLabel ?? msg.inputClear }) : null,
1673
1805
  /* @__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
1806
  ]
1675
1807
  }
@@ -1693,10 +1825,45 @@ function DatePicker({
1693
1825
  }
1694
1826
 
1695
1827
  // src/components/forms/DateRangePicker.tsx
1828
+ var React16 = __toESM(require("react"), 1);
1696
1829
  var import_react_aria_components19 = require("react-aria-components");
1697
1830
  var import_jsx_runtime27 = require("react/jsx-runtime");
1831
+ function coerceRange(range) {
1832
+ if (range === void 0) {
1833
+ return void 0;
1834
+ }
1835
+ if (range === null) {
1836
+ return null;
1837
+ }
1838
+ const start = coerceDateValue(range.start) ?? null;
1839
+ const end = coerceDateValue(range.end) ?? null;
1840
+ return start === null && end === null ? null : { start, end };
1841
+ }
1698
1842
  function rangeLengthInDays(range) {
1699
- return range.end.toDate("UTC").getTime() / 864e5 - range.start.toDate("UTC").getTime() / 864e5 + 1;
1843
+ const start = coerceDateValue(range?.start) ?? null;
1844
+ const end = coerceDateValue(range?.end) ?? null;
1845
+ if (start === null || end === null) {
1846
+ return null;
1847
+ }
1848
+ return end.toDate("UTC").getTime() / 864e5 - start.toDate("UTC").getTime() / 864e5 + 1;
1849
+ }
1850
+ function ClearButton3({ label }) {
1851
+ const state = React16.useContext(import_react_aria_components19.DateRangePickerStateContext);
1852
+ if (!state?.value?.start && !state?.value?.end) {
1853
+ return null;
1854
+ }
1855
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1856
+ import_react_aria_components19.Button,
1857
+ {
1858
+ className: "pire-input-clear",
1859
+ slot: null,
1860
+ onPress: (event) => {
1861
+ state.setValue(null);
1862
+ focusFieldOf(event.target);
1863
+ },
1864
+ children: label
1865
+ }
1866
+ );
1700
1867
  }
1701
1868
  function DateRangePicker({
1702
1869
  label,
@@ -1710,11 +1877,30 @@ function DateRangePicker({
1710
1877
  endName,
1711
1878
  className,
1712
1879
  style,
1880
+ value,
1881
+ defaultValue,
1882
+ onChange,
1883
+ valueFormat,
1884
+ minValue,
1885
+ maxValue,
1886
+ isClearable,
1887
+ clearLabel,
1713
1888
  ...rest
1714
1889
  }) {
1715
1890
  const msg = usePireMessages();
1716
- const current = rest.value ?? rest.defaultValue ?? null;
1717
- const tooLong = maxRangeDays != null && current != null && rangeLengthInDays(current) > maxRangeDays;
1891
+ const current = coerceRange(value) ?? coerceRange(defaultValue) ?? null;
1892
+ const span = current === null ? null : rangeLengthInDays(current);
1893
+ const tooLong = maxRangeDays != null && span != null && span > maxRangeDays;
1894
+ const emit = (next) => {
1895
+ if (!onChange) {
1896
+ return;
1897
+ }
1898
+ if (!next?.start || !next.end) {
1899
+ onChange(null);
1900
+ return;
1901
+ }
1902
+ onChange(valueFormat === "iso" ? { start: next.start.toString(), end: next.end.toString() } : { start: next.start, end: next.end });
1903
+ };
1718
1904
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1719
1905
  import_react_aria_components19.DateRangePicker,
1720
1906
  {
@@ -1723,8 +1909,13 @@ function DateRangePicker({
1723
1909
  startName,
1724
1910
  endName,
1725
1911
  validationBehavior: "aria",
1912
+ value: coerceRange(value),
1913
+ defaultValue: coerceRange(defaultValue),
1914
+ minValue: coerceBound(minValue),
1915
+ maxValue: coerceBound(maxValue),
1916
+ onChange: emit,
1726
1917
  ...rest,
1727
- isInvalid: rest.isInvalid || tooLong,
1918
+ isInvalid: rest.isInvalid || tooLong || void 0,
1728
1919
  children: [
1729
1920
  label ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_react_aria_components19.Label, { className: "pire-field-label", children: [
1730
1921
  label,
@@ -1742,6 +1933,7 @@ function DateRangePicker({
1742
1933
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_aria_components19.DateInput, { slot: "start", className: "pire-datefield", children: (segment) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_aria_components19.DateSegment, { segment, className: "pire-datesegment" }) }),
1743
1934
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": "true", className: "pire-daterange-dash", children: "\u2013" }),
1744
1935
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_aria_components19.DateInput, { slot: "end", className: "pire-datefield", children: (segment) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_aria_components19.DateSegment, { segment, className: "pire-datesegment" }) }),
1936
+ isClearable && !rest.isDisabled && !rest.isReadOnly ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ClearButton3, { label: clearLabel ?? msg.inputClear }) : null,
1745
1937
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1746
1938
  import_react_aria_components19.Button,
1747
1939
  {
@@ -1765,7 +1957,7 @@ function DateRangePicker({
1765
1957
  import_react_aria_components19.Button,
1766
1958
  {
1767
1959
  className: "pire-daterange-preset",
1768
- onPress: () => rest.onChange?.(preset.range()),
1960
+ onPress: () => emit(coerceRange(preset.range()) ?? null),
1769
1961
  children: preset.label
1770
1962
  },
1771
1963
  preset.id
@@ -1807,13 +1999,13 @@ function DateRangePicker({
1807
1999
  }
1808
2000
 
1809
2001
  // src/components/forms/ValueHelpField.tsx
1810
- var React16 = __toESM(require("react"), 1);
2002
+ var React19 = __toESM(require("react"), 1);
1811
2003
 
1812
2004
  // src/components/patterns/ValueHelpDialog.tsx
1813
- var React15 = __toESM(require("react"), 1);
2005
+ var React18 = __toESM(require("react"), 1);
1814
2006
 
1815
2007
  // src/components/feedback/Dialog.tsx
1816
- var React14 = __toESM(require("react"), 1);
2008
+ var React17 = __toESM(require("react"), 1);
1817
2009
  var import_react_aria_components20 = require("react-aria-components");
1818
2010
  var import_jsx_runtime28 = require("react/jsx-runtime");
1819
2011
  function DialogTitle({ children, className }) {
@@ -1847,8 +2039,8 @@ function Dialog({
1847
2039
  onClose?.();
1848
2040
  onOpenChange?.(false);
1849
2041
  };
1850
- const parts = React14.Children.toArray(children);
1851
- const slotOf = (type) => parts.find((node) => React14.isValidElement(node) && node.type === type);
2042
+ const parts = React17.Children.toArray(children);
2043
+ const slotOf = (type) => parts.find((node) => React17.isValidElement(node) && node.type === type);
1852
2044
  const titleSlot = slotOf(DialogTitle);
1853
2045
  const descriptionSlot = slotOf(DialogDescription);
1854
2046
  const bodySlot = slotOf(DialogBody);
@@ -2097,11 +2289,11 @@ function ValueHelpDialog({
2097
2289
  onClose
2098
2290
  }) {
2099
2291
  const msg = usePireMessages();
2100
- const [query, setQuery] = React15.useState("");
2101
- React15.useEffect(() => {
2292
+ const [query, setQuery] = React18.useState("");
2293
+ React18.useEffect(() => {
2102
2294
  if (isOpen) setQuery("");
2103
2295
  }, [isOpen]);
2104
- const filtered = React15.useMemo(() => {
2296
+ const filtered = React18.useMemo(() => {
2105
2297
  if (isFilteredExternally) return rows;
2106
2298
  const q = query.trim().toLowerCase();
2107
2299
  if (!q) return rows;
@@ -2183,7 +2375,7 @@ function ValueHelpField({
2183
2375
  style
2184
2376
  }) {
2185
2377
  const msg = usePireMessages();
2186
- const [open, setOpen] = React16.useState(false);
2378
+ const [open, setOpen] = React19.useState(false);
2187
2379
  return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cx(className), style, children: [
2188
2380
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
2189
2381
  Input,
@@ -2239,7 +2431,7 @@ function ValueHelpField({
2239
2431
  }
2240
2432
 
2241
2433
  // src/components/forms/FileUpload.tsx
2242
- var React17 = __toESM(require("react"), 1);
2434
+ var React20 = __toESM(require("react"), 1);
2243
2435
  var import_react_aria_components23 = require("react-aria-components");
2244
2436
 
2245
2437
  // src/components/feedback/ProgressBar.tsx
@@ -2330,10 +2522,10 @@ function FileUpload({
2330
2522
  ...rest
2331
2523
  }) {
2332
2524
  const msg = usePireMessages();
2333
- const [uncontrolled, setUncontrolled] = React17.useState(defaultValue ?? []);
2525
+ const [uncontrolled, setUncontrolled] = React20.useState(defaultValue ?? []);
2334
2526
  const files = value ?? uncontrolled;
2335
- const created = React17.useRef([]);
2336
- React17.useEffect(() => () => {
2527
+ const created = React20.useRef([]);
2528
+ React20.useEffect(() => () => {
2337
2529
  for (const url of created.current) URL.revokeObjectURL(url);
2338
2530
  }, []);
2339
2531
  const commit = (next) => {
@@ -2445,7 +2637,7 @@ function ShellBar({
2445
2637
  }
2446
2638
 
2447
2639
  // src/components/navigation/SideNav.tsx
2448
- var React18 = __toESM(require("react"), 1);
2640
+ var React21 = __toESM(require("react"), 1);
2449
2641
  var import_react_aria_components26 = require("react-aria-components");
2450
2642
 
2451
2643
  // src/components/navigation/Menu.tsx
@@ -2532,10 +2724,10 @@ function SideNav({
2532
2724
  const msg = usePireMessages();
2533
2725
  const activeParent = findActiveParent(groups, value);
2534
2726
  const isControlled = expandedIds != null;
2535
- const [ownExpanded, setOwnExpanded] = React18.useState(
2727
+ const [ownExpanded, setOwnExpanded] = React21.useState(
2536
2728
  () => new Set(defaultExpandedIds ?? (activeParent ? [activeParent] : []))
2537
2729
  );
2538
- const [lastValue, setLastValue] = React18.useState(value);
2730
+ const [lastValue, setLastValue] = React21.useState(value);
2539
2731
  if (value !== lastValue) {
2540
2732
  setLastValue(value);
2541
2733
  if (!isControlled && activeParent && !ownExpanded.has(activeParent)) {
@@ -2799,7 +2991,7 @@ function InlineMessage({ kind = "info", title, action, onClose, children, classN
2799
2991
  }
2800
2992
 
2801
2993
  // src/components/feedback/Toast.tsx
2802
- var React19 = __toESM(require("react"), 1);
2994
+ var React22 = __toESM(require("react"), 1);
2803
2995
  var import_jsx_runtime42 = require("react/jsx-runtime");
2804
2996
  var KIND_ICON2 = {
2805
2997
  info: "info",
@@ -2825,26 +3017,26 @@ function Toast({ kind = "success", onClose, position = "bottom-center", children
2825
3017
  }
2826
3018
  );
2827
3019
  }
2828
- var ToastContext = React19.createContext(null);
3020
+ var ToastContext = React22.createContext(null);
2829
3021
  function ToastProvider({ children, timeout = 6e3 }) {
2830
3022
  const msg = usePireMessages();
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) => {
3023
+ const [toasts, setToasts] = React22.useState([]);
3024
+ const close = React22.useCallback((id) => setToasts((t) => t.filter((x) => x.id !== id)), []);
3025
+ const add = React22.useCallback((toast) => {
2834
3026
  const id = Math.random().toString(36).slice(2);
2835
3027
  setToasts((t) => [...t, { ...toast, id }]);
2836
3028
  const ms = toast.timeout ?? timeout;
2837
3029
  if (ms > 0) setTimeout(() => close(id), ms);
2838
3030
  return id;
2839
3031
  }, [close, timeout]);
2840
- const value = React19.useMemo(() => ({ add, close }), [add, close]);
3032
+ const value = React22.useMemo(() => ({ add, close }), [add, close]);
2841
3033
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(ToastContext.Provider, { value, children: [
2842
3034
  children,
2843
3035
  /* @__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)) })
2844
3036
  ] });
2845
3037
  }
2846
3038
  function useToast() {
2847
- const ctx = React19.useContext(ToastContext);
3039
+ const ctx = React22.useContext(ToastContext);
2848
3040
  if (!ctx) throw new Error("useToast must be used inside <ToastProvider>");
2849
3041
  return ctx;
2850
3042
  }
@@ -3194,14 +3386,14 @@ function BarChart({
3194
3386
  }
3195
3387
 
3196
3388
  // src/components/data/LineChart.tsx
3197
- var React20 = __toESM(require("react"), 1);
3389
+ var React23 = __toESM(require("react"), 1);
3198
3390
  var import_jsx_runtime50 = require("react/jsx-runtime");
3199
3391
  var PLOT_INSET = { top: 10, right: 14, bottom: 24, left: 58 };
3200
3392
  var MARKER_LIMIT = 20;
3201
- var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? React20.useEffect : React20.useLayoutEffect;
3393
+ var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? React23.useEffect : React23.useLayoutEffect;
3202
3394
  function useMeasuredWidth() {
3203
- const ref = React20.useRef(null);
3204
- const [width, setWidth] = React20.useState(0);
3395
+ const ref = React23.useRef(null);
3396
+ const [width, setWidth] = React23.useState(0);
3205
3397
  useIsomorphicLayoutEffect2(() => {
3206
3398
  const element = ref.current;
3207
3399
  if (!element) return;
@@ -3364,7 +3556,7 @@ function LineChart({
3364
3556
  }
3365
3557
 
3366
3558
  // src/components/data/DonutChart.tsx
3367
- var React21 = __toESM(require("react"), 1);
3559
+ var React24 = __toESM(require("react"), 1);
3368
3560
  var import_jsx_runtime51 = require("react/jsx-runtime");
3369
3561
  var SEGMENT_GAP_DEGREES = 1.5;
3370
3562
  function DonutChart({
@@ -3385,7 +3577,7 @@ function DonutChart({
3385
3577
  }) {
3386
3578
  const label = rest["aria-label"];
3387
3579
  const msg = usePireMessages();
3388
- const [hoveredIndex, setHoveredIndex] = React21.useState(null);
3580
+ const [hoveredIndex, setHoveredIndex] = React24.useState(null);
3389
3581
  warnOnDonutShape(series, categories.length, label);
3390
3582
  const values = (series[0]?.values ?? []).slice(0, categories.length);
3391
3583
  const segments = categories.map((category, index) => ({ category, index, value: values[index] ?? 0 })).filter((segment) => segment.value > 0);
@@ -3622,7 +3814,7 @@ function ConfirmDialog({
3622
3814
  }
3623
3815
 
3624
3816
  // src/components/patterns/CommandPalette.tsx
3625
- var React22 = __toESM(require("react"), 1);
3817
+ var React25 = __toESM(require("react"), 1);
3626
3818
  var import_react_aria_components31 = require("react-aria-components");
3627
3819
  var import_jsx_runtime60 = require("react/jsx-runtime");
3628
3820
  var RESULTS_SECTION_KEY = "__pire_results__";
@@ -3645,17 +3837,17 @@ function CommandPalette({
3645
3837
  style
3646
3838
  }) {
3647
3839
  const msg = usePireMessages();
3648
- const [query, setQuery] = React22.useState("");
3840
+ const [query, setQuery] = React25.useState("");
3649
3841
  const { contains } = (0, import_react_aria_components31.useFilter)({ sensitivity: "base" });
3650
- React22.useEffect(() => {
3842
+ React25.useEffect(() => {
3651
3843
  if (isOpen) setQuery("");
3652
3844
  }, [isOpen]);
3653
- const searchableById = React22.useMemo(() => {
3845
+ const searchableById = React25.useMemo(() => {
3654
3846
  const byId = /* @__PURE__ */ new Map();
3655
3847
  for (const group of groups) for (const item of group.items) byId.set(item.id, searchableText(item));
3656
3848
  return byId;
3657
3849
  }, [groups]);
3658
- const filter = React22.useCallback((textValue, inputValue, node) => {
3850
+ const filter = React25.useCallback((textValue, inputValue, node) => {
3659
3851
  if (node.parentKey === RESULTS_SECTION_KEY) return true;
3660
3852
  return contains(searchableById.get(String(node.key)) ?? textValue, inputValue);
3661
3853
  }, [contains, searchableById]);
@@ -3738,9 +3930,9 @@ function CommandPalette({
3738
3930
  );
3739
3931
  }
3740
3932
  function useCommandPaletteShortcut(onTrigger, { key = "k", isDisabled } = {}) {
3741
- const handler = React22.useRef(onTrigger);
3933
+ const handler = React25.useRef(onTrigger);
3742
3934
  handler.current = onTrigger;
3743
- React22.useEffect(() => {
3935
+ React25.useEffect(() => {
3744
3936
  if (isDisabled) return;
3745
3937
  const onKeyDown = (event) => {
3746
3938
  if (!(event.metaKey || event.ctrlKey) || event.altKey) return;
@@ -3794,7 +3986,7 @@ function EmptyState({ icon = "file-text", title, action, compact, children, clas
3794
3986
  }
3795
3987
 
3796
3988
  // src/components/patterns/FileDropZone.tsx
3797
- var React23 = __toESM(require("react"), 1);
3989
+ var React26 = __toESM(require("react"), 1);
3798
3990
  var import_react_aria_components33 = require("react-aria-components");
3799
3991
  var import_jsx_runtime63 = require("react/jsx-runtime");
3800
3992
  var isImage2 = (file) => file.type.startsWith("image/");
@@ -3824,10 +4016,10 @@ function FileDropZone({
3824
4016
  ...rest
3825
4017
  }) {
3826
4018
  const msg = usePireMessages();
3827
- const [uncontrolled, setUncontrolled] = React23.useState(defaultValue ?? []);
4019
+ const [uncontrolled, setUncontrolled] = React26.useState(defaultValue ?? []);
3828
4020
  const files = value ?? uncontrolled;
3829
- const created = React23.useRef([]);
3830
- React23.useEffect(() => () => {
4021
+ const created = React26.useRef([]);
4022
+ React26.useEffect(() => () => {
3831
4023
  for (const url of created.current) URL.revokeObjectURL(url);
3832
4024
  }, []);
3833
4025
  const commit = (next) => {