@ballistix.digital/react-components 9.10.0 → 9.10.1-rc-410.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
@@ -12,6 +12,7 @@ var $iA2ta$fortawesomereactfontawesome = require("@fortawesome/react-fontawesome
12
12
  var $iA2ta$floatinguireact = require("@floating-ui/react");
13
13
  var $iA2ta$headlessuireact = require("@headlessui/react");
14
14
  var $iA2ta$reactinputmask = require("@react-input/mask");
15
+ var $iA2ta$reactnumberformat = require("react-number-format");
15
16
  var $iA2ta$tanstackreacttable = require("@tanstack/react-table");
16
17
  var $iA2ta$reacttailwindcssselect = require("react-tailwindcss-select");
17
18
  var $iA2ta$xlsx = require("xlsx");
@@ -1361,6 +1362,7 @@ var $ccdca29d489f3200$export$2e2bcd8739ae039 = $ccdca29d489f3200$var$ProgressTra
1361
1362
 
1362
1363
 
1363
1364
 
1365
+
1364
1366
  const $3369f7d596d23d58$var$base = {
1365
1367
  foot: '',
1366
1368
  hint: 'text-gray-400 text-xs mt-1',
@@ -1650,7 +1652,7 @@ var $47f4a9343fdccb9b$export$2e2bcd8739ae039 = $47f4a9343fdccb9b$var$styles;
1650
1652
 
1651
1653
 
1652
1654
  const $c474e734ef48f77c$var$InputGroup = (props)=>{
1653
- const { name: name, htmlType: htmlType = 'text', label: label, hint: hint, description: description, placeholder: placeholder, leading: leading, trailing: trailing, required: required, value: value, mask: mask, min: min, max: max, step: step, maxLength: maxLength, rows: rows, error: error, type: type = 'normal', isDisabled: isDisabled, isRequired: isRequired = false, isTouched: isTouched = false, isSolo: isSolo = false, isOptionalLabelHidden: isOptionalLabelHidden = false, onChange: onChange, onClear: onClear, onBlur: onBlur, onClick: onClick, onKeyDown: onKeyDown, setRefOnLoad: setRefOnLoad, styles: stylesOverrides } = props;
1655
+ const { name: name, htmlType: htmlType = 'text', label: label, hint: hint, description: description, placeholder: placeholder, leading: leading, trailing: trailing, required: required, value: value, mask: mask, min: min, max: max, step: step, maxLength: maxLength, rows: rows, error: error, precision: precision, thousandSeparator: thousandSeparator, decimalSeparator: decimalSeparator, currency: currency, type: type = 'normal', isDisabled: isDisabled, isRequired: isRequired = false, isTouched: isTouched = false, isSolo: isSolo = false, isOptionalLabelHidden: isOptionalLabelHidden = false, onChange: onChange, onClear: onClear, onBlur: onBlur, onClick: onClick, onKeyDown: onKeyDown, setRefOnLoad: setRefOnLoad, styles: stylesOverrides } = props;
1654
1656
  const isValid = error === undefined;
1655
1657
  const maskRef = (0, $iA2ta$reactinputmask.useMask)({
1656
1658
  mask: mask?.mask,
@@ -1660,6 +1662,74 @@ const $c474e734ef48f77c$var$InputGroup = (props)=>{
1660
1662
  });
1661
1663
  const defaultRef = (0, $iA2ta$react.useRef)(null);
1662
1664
  const ref = mask ? maskRef : defaultRef;
1665
+ const isNumeric = htmlType === 'number' || htmlType === 'percent' || htmlType === 'currency';
1666
+ const numericValueRef = (0, $iA2ta$react.useRef)(null);
1667
+ const numericFormatProps = (0, $iA2ta$react.useMemo)(()=>{
1668
+ if (htmlType === 'percent') return {
1669
+ decimalScale: precision,
1670
+ suffix: '%'
1671
+ };
1672
+ if (htmlType === 'currency') {
1673
+ const resolvedDecimalSeparator = decimalSeparator ?? ',';
1674
+ return {
1675
+ decimalScale: precision ?? 2,
1676
+ fixedDecimalScale: true,
1677
+ thousandSeparator: thousandSeparator ?? '.',
1678
+ decimalSeparator: resolvedDecimalSeparator,
1679
+ // Numpad decimal keys are typically ".", regardless of locale. A literal "." can never be a
1680
+ // manually-typed thousand separator (grouping is auto-inserted), so it's safe to also accept
1681
+ // it as a decimal keystroke even when the configured decimal separator is something else.
1682
+ allowedDecimalSeparators: Array.from(new Set([
1683
+ resolvedDecimalSeparator,
1684
+ '.'
1685
+ ])),
1686
+ prefix: `${currency ?? "\u20AC"} `
1687
+ };
1688
+ }
1689
+ // htmlType === 'number'
1690
+ return {
1691
+ decimalScale: precision,
1692
+ thousandSeparator: thousandSeparator,
1693
+ allowNegative: min === undefined || Number(min) < 0
1694
+ };
1695
+ }, [
1696
+ htmlType,
1697
+ precision,
1698
+ thousandSeparator,
1699
+ decimalSeparator,
1700
+ currency,
1701
+ min
1702
+ ]);
1703
+ const handleNumericValueChange = (values, sourceInfo)=>{
1704
+ numericValueRef.current = values.floatValue ?? null;
1705
+ if (sourceInfo.source !== 'event') return;
1706
+ onChange(values.floatValue ?? null, values.formattedValue);
1707
+ };
1708
+ const handleNumericBlur = (e)=>{
1709
+ if (htmlType === 'number' && numericValueRef.current !== null) {
1710
+ let clamped = numericValueRef.current;
1711
+ if (min !== undefined) clamped = Math.max(Number(min), clamped);
1712
+ if (max !== undefined) clamped = Math.min(Number(max), clamped);
1713
+ if (clamped !== numericValueRef.current) {
1714
+ numericValueRef.current = clamped;
1715
+ onChange(clamped, String(clamped));
1716
+ }
1717
+ }
1718
+ onBlur(e);
1719
+ };
1720
+ const handleNumericKeyDown = (e)=>{
1721
+ if (htmlType === 'number' && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
1722
+ e.preventDefault();
1723
+ const stepValue = step !== undefined && step !== 'any' ? Number(step) : 1;
1724
+ const delta = stepValue * (e.key === 'ArrowUp' ? 1 : -1);
1725
+ let next = (numericValueRef.current ?? 0) + delta;
1726
+ if (min !== undefined) next = Math.max(Number(min), next);
1727
+ if (max !== undefined) next = Math.min(Number(max), next);
1728
+ numericValueRef.current = next;
1729
+ onChange(next, String(next));
1730
+ }
1731
+ onKeyDown?.(e);
1732
+ };
1663
1733
  const handleGenerateStyle = ()=>{
1664
1734
  const result = (0, $f0c671820c340322$export$e2d2075c69d9100d)((0, $47f4a9343fdccb9b$export$2e2bcd8739ae039).base);
1665
1735
  const keys = (0, $iA2ta$lodash.concat)((0, $27f90b0cff19565d$export$be5d6ab3c5b84767)((0, $47f4a9343fdccb9b$export$2e2bcd8739ae039).base), (0, $27f90b0cff19565d$export$be5d6ab3c5b84767)((0, $3369f7d596d23d58$export$2e2bcd8739ae039).base), (0, $27f90b0cff19565d$export$be5d6ab3c5b84767)((0, $65bb4d593718fcda$export$2e2bcd8739ae039).base));
@@ -1704,7 +1774,7 @@ const $c474e734ef48f77c$var$InputGroup = (props)=>{
1704
1774
  className: styles.leading,
1705
1775
  children: leading
1706
1776
  }),
1707
- !htmlType || htmlType !== 'area' && (0, $iA2ta$reactjsxruntime.jsxs)("div", {
1777
+ !htmlType || htmlType !== 'area' && !isNumeric && (0, $iA2ta$reactjsxruntime.jsxs)("div", {
1708
1778
  className: "flex items-center",
1709
1779
  children: [
1710
1780
  (0, $iA2ta$reactjsxruntime.jsx)("input", {
@@ -1745,6 +1815,27 @@ const $c474e734ef48f77c$var$InputGroup = (props)=>{
1745
1815
  })
1746
1816
  ]
1747
1817
  }),
1818
+ isNumeric && (0, $iA2ta$reactjsxruntime.jsx)("div", {
1819
+ className: "flex items-center",
1820
+ children: (0, $iA2ta$reactjsxruntime.jsx)((0, $iA2ta$reactnumberformat.NumericFormat), {
1821
+ getInputRef: ref,
1822
+ name: name,
1823
+ id: name,
1824
+ "data-cy": inputDataCy,
1825
+ className: styles.input,
1826
+ placeholder: placeholder,
1827
+ defaultValue: isSolo ? value : undefined,
1828
+ value: isSolo ? undefined : value,
1829
+ valueIsNumericString: true,
1830
+ inputMode: "decimal",
1831
+ disabled: isDisabled,
1832
+ onValueChange: handleNumericValueChange,
1833
+ onBlur: handleNumericBlur,
1834
+ onClick: onClick,
1835
+ onKeyDown: handleNumericKeyDown,
1836
+ ...numericFormatProps
1837
+ })
1838
+ }),
1748
1839
  htmlType === 'area' && (0, $iA2ta$reactjsxruntime.jsx)("textarea", {
1749
1840
  rows: rows,
1750
1841
  name: name,
@@ -2233,14 +2324,14 @@ const $0c7305af577f9538$var$base = {
2233
2324
  input: 'w-full flex border',
2234
2325
  menuButton: '',
2235
2326
  menu: 'z-10 px-0! py-0! w-full! rounded-md overflow-hidden border mt-1! absolute bg-white',
2236
- tagItem: 'bg-gray-200 px-2 py-1 flex flex-row gap-x-1 items-center justify-between rounded-xs',
2327
+ tagItem: 'bg-gray-200 px-2 py-1 flex flex-row gap-x-1 items-center justify-between rounded-md',
2237
2328
  tagItemText: 'text-xs',
2238
2329
  tagItemIconContainer: 'p-0! rounded-full hover:bg-gray-400 text-sm hover:text-white aspect-square w-4 flex items-center justify-center cursor-pointer',
2239
2330
  tagItemIcon: '',
2240
2331
  list: 'px-0! w-full! *:px-0 max-h-56 overflow-scroll py-1!',
2241
2332
  listGroupLabel: '',
2242
2333
  listItem: 'w-full! list-none! hover:bg-primary-500 hover:text-white py-2! px-2! cursor-pointer',
2243
- listDisabledItem: '',
2334
+ listDisabledItem: 'w-full! list-none! py-2! px-2! cursor-not-allowed text-gray-400',
2244
2335
  searchContainer: 'w-full! px-2! py-2! border-b',
2245
2336
  searchBox: '',
2246
2337
  searchIcon: '',
@@ -2297,7 +2388,7 @@ var $0c7305af577f9538$export$2e2bcd8739ae039 = $0c7305af577f9538$var$styles;
2297
2388
 
2298
2389
 
2299
2390
  const $cb1b9d36d0757897$var$SelectMenu = (props)=>{
2300
- const { name: name = 'select-menu-form', label: label, description: description, hint: hint, placeholder: placeholder, options: options, required: required, value: value, error: error, isRequired: isRequired = false, isLoading: isLoading, isTouched: isTouched = false, isDisabled: isDisabled, isClearable: isClearable = true, isMultiple: isMultiple = false, isSearchable: isSearchable = true, isSolo: isSolo = false, isOptionalLabelHidden: isOptionalLabelHidden = false, onChange: onChange, onSearchInputChange: onSearchInputChange, onClear: onClear, onBlur: onBlur, searchInputOptionRenderer: searchInputOptionRenderer, dataTestId: dataTestId, styles: stylesOverrides } = props;
2391
+ const { name: name = 'select-menu-form', label: label, description: description, hint: hint, placeholder: placeholder, options: options, required: required, value: value, error: error, isRequired: isRequired = false, isLoading: isLoading, isTouched: isTouched = false, isDisabled: isDisabled, isClearable: isClearable = true, isMultiple: isMultiple = false, isSearchable: isSearchable = true, isSolo: isSolo = false, isOptionalLabelHidden: isOptionalLabelHidden = false, onChange: onChange, onSearchInputChange: onSearchInputChange, onClear: onClear, onBlur: onBlur, optionRenderer: optionRenderer, searchInputOptionRenderer: searchInputOptionRenderer, dataTestId: dataTestId, styles: stylesOverrides } = props;
2301
2392
  const [isFocus, setIsFocus] = (0, $iA2ta$react.useState)(true);
2302
2393
  const [state, setState] = (0, $iA2ta$react.useState)(value ?? null);
2303
2394
  const [searchValue, setSearchValue] = (0, $iA2ta$react.useState)();
@@ -2311,16 +2402,6 @@ const $cb1b9d36d0757897$var$SelectMenu = (props)=>{
2311
2402
  });
2312
2403
  return result;
2313
2404
  };
2314
- const handleChange = (selectValue)=>{
2315
- setState(selectValue);
2316
- onChange && onChange(selectValue);
2317
- setIsFocus(false);
2318
- if (searchInputOptionRenderer) setSearchValue(null);
2319
- };
2320
- const handleSearchInputChange = (e)=>{
2321
- if (searchInputOptionRenderer) setSearchValue(e.target.value);
2322
- onSearchInputChange?.(e);
2323
- };
2324
2405
  const computedOptions = (0, $iA2ta$react.useMemo)(()=>{
2325
2406
  if (!searchInputOptionRenderer || !searchValue) return options;
2326
2407
  if (options.some((opt)=>opt.label.toLowerCase().includes(searchValue.toLowerCase()))) return options;
@@ -2337,6 +2418,26 @@ const $cb1b9d36d0757897$var$SelectMenu = (props)=>{
2337
2418
  options,
2338
2419
  searchValue
2339
2420
  ]);
2421
+ const optionsByValue = (0, $iA2ta$react.useMemo)(()=>(0, $iA2ta$lodash.keyBy)(computedOptions, 'value'), [
2422
+ computedOptions
2423
+ ]);
2424
+ const handleChange = (selectValue)=>{
2425
+ const applyValue = (nextValue)=>{
2426
+ setState(nextValue);
2427
+ onChange && onChange(nextValue);
2428
+ setIsFocus(false);
2429
+ if (searchInputOptionRenderer) setSearchValue(null);
2430
+ };
2431
+ // A single selection is handed back straight from `options`.
2432
+ if (!Array.isArray(selectValue)) return applyValue(selectValue);
2433
+ // A multiple selection echoes the value we passed in, which carries a rendered `label`
2434
+ // (see `libraryValue`), so resolve those entries to the options we were given.
2435
+ return applyValue(selectValue.map((option)=>optionsByValue[option.value] ?? option));
2436
+ };
2437
+ const handleSearchInputChange = (e)=>{
2438
+ if (searchInputOptionRenderer) setSearchValue(e.target.value);
2439
+ onSearchInputChange?.(e);
2440
+ };
2340
2441
  const styles = handleGenerateStyle();
2341
2442
  // Simulate onClear event.
2342
2443
  (0, $iA2ta$react.useEffect)(()=>{
@@ -2386,8 +2487,10 @@ const $cb1b9d36d0757897$var$SelectMenu = (props)=>{
2386
2487
  name
2387
2488
  ]);
2388
2489
  const formattedError = (0, $iA2ta$react.useMemo)(()=>{
2389
- if (error) return JSON.stringify(error).replaceAll('"', '');
2390
- return null;
2490
+ if (!error) return null;
2491
+ if (typeof error === 'string') return error;
2492
+ if ((0, $iA2ta$lodash.isArray)(error)) return (0, $iA2ta$lodash.join)((0, $iA2ta$lodash.map)(error, 'label'), ', ');
2493
+ return error.label;
2391
2494
  }, [
2392
2495
  error
2393
2496
  ]);
@@ -2406,10 +2509,30 @@ const $cb1b9d36d0757897$var$SelectMenu = (props)=>{
2406
2509
  });
2407
2510
  }
2408
2511
  return (0, $iA2ta$reactjsxruntime.jsx)("div", {
2409
- className: styles.listItem,
2410
- children: data.label
2512
+ role: "option",
2513
+ "aria-selected": !!data.isSelected,
2514
+ className: data.disabled ? styles.listDisabledItem : styles.listItem,
2515
+ // Formatted options are rendered inside a wrapper that carries the select's own click
2516
+ // handler, which bypasses its disabled handling.
2517
+ onClick: (e)=>data.disabled && e.stopPropagation(),
2518
+ children: optionRenderer ? optionRenderer(data) : data.label
2411
2519
  });
2412
2520
  };
2521
+ // The select renders `label` as children everywhere except its search filter, which only ever
2522
+ // reads `options`. Rendering the value keeps the selected button and tags in sync with the list.
2523
+ const libraryValue = (0, $iA2ta$react.useMemo)(()=>{
2524
+ if (!optionRenderer || !state) return state;
2525
+ return (0, $iA2ta$lodash.isArray)(state) ? (0, $iA2ta$lodash.map)(state, (option)=>({
2526
+ ...option,
2527
+ label: optionRenderer(option)
2528
+ })) : {
2529
+ ...state,
2530
+ label: optionRenderer(state)
2531
+ };
2532
+ }, [
2533
+ optionRenderer,
2534
+ state
2535
+ ]);
2413
2536
  return (0, $iA2ta$reactjsxruntime.jsxs)("div", {
2414
2537
  className: styles.container,
2415
2538
  "data-testid": selectDataTestId,
@@ -2450,11 +2573,11 @@ const $cb1b9d36d0757897$var$SelectMenu = (props)=>{
2450
2573
  //
2451
2574
  options: computedOptions,
2452
2575
  //
2453
- value: state,
2576
+ value: libraryValue,
2454
2577
  onChange: handleChange,
2455
2578
  onSearchInputChange: handleSearchInputChange,
2456
2579
  //
2457
- formatOptionLabel: searchInputOptionRenderer ? handleFormatOptionLabel : undefined,
2580
+ formatOptionLabel: searchInputOptionRenderer || optionRenderer ? handleFormatOptionLabel : undefined,
2458
2581
  classNames: {
2459
2582
  menuButton: ()=>(0, $622cd2936b18c771$export$4370d69198e9314a)(styles.input, `bx-select-menu-${name}`, state === null && '*:text-gray-300! ', state === null && !isValid && isTouched && '*:text-red-300!'),
2460
2583
  menu: styles.menu,
@@ -6410,7 +6533,7 @@ var $df1ad8019f5459e8$export$2e2bcd8739ae039 = $df1ad8019f5459e8$var$styles;
6410
6533
 
6411
6534
 
6412
6535
  const $a8e0e51381520cae$var$FileInputGroup = (props)=>{
6413
- const { name: name = 'multiple-file-input', label: label, description: description, hint: hint, required: required, value: value, max: max, error: error, isDisabled: isDisabled, isRequired: isRequired = false, isTouched: isTouched = false, isMulti: isMulti = false, children: children, onChange: onChange, onBlur: onBlur, styles: stylesOverrides } = props;
6536
+ const { name: name = 'multiple-file-input', label: label, description: description, hint: hint, required: required, value: value, max: max, accept: accept, error: error, isDisabled: isDisabled, isRequired: isRequired = false, isTouched: isTouched = false, isMulti: isMulti = false, children: children, onChange: onChange, onBlur: onBlur, styles: stylesOverrides } = props;
6414
6537
  const isValid = error === undefined;
6415
6538
  const [isDragging, setIsDragging] = (0, $iA2ta$react.useState)(false);
6416
6539
  const handleChangeInput = (0, $iA2ta$react.useCallback)((files)=>{
@@ -6449,6 +6572,7 @@ const $a8e0e51381520cae$var$FileInputGroup = (props)=>{
6449
6572
  onDrop: onDrop,
6450
6573
  multiple: isMulti,
6451
6574
  disabled: isDisabled,
6575
+ accept: accept,
6452
6576
  onError: ()=>{
6453
6577
  // console.log('error');
6454
6578
  // setFieldError('file', t('component.form.error.multipleFiles') as string);