@jars-labs/jars-components 1.2.0 → 1.3.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.
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { Accordion as Accordion$1, AccordionItem as AccordionItem$1, AccordionControl as AccordionControl$1, AccordionPanel as AccordionPanel$1, ActionIcon as ActionIcon$1, Group, Combobox, useCombobox, Flex, TextInput, Loader as Loader$1, Avatar as Avatar$1, Checkbox as Checkbox$1, Menu as Menu$1, Divider, Modal as Modal$1, Tooltip as Tooltip$1, Box, Tabs as Tabs$1, Pagination as Pagination$1, Radio, SegmentedControl as SegmentedControl$1, Select as Select$1, Stepper as Stepper$1, StepperStep, Progress, Table as Table$1, Textarea as Textarea$1 } from '@mantine/core';
2
+ import { Accordion as Accordion$1, AccordionItem as AccordionItem$1, AccordionControl as AccordionControl$1, AccordionPanel as AccordionPanel$1, ActionIcon as ActionIcon$1, Group, Loader as Loader$1, Combobox, useCombobox, Flex, TextInput, Avatar as Avatar$1, Checkbox as Checkbox$1, Menu as Menu$1, Divider, Modal as Modal$1, Tooltip as Tooltip$1, Box, Tabs as Tabs$1, Pagination as Pagination$1, Radio, SegmentedControl as SegmentedControl$1, Select as Select$1, Stepper as Stepper$1, StepperStep, Progress, Table as Table$1, Textarea as Textarea$1 } from '@mantine/core';
3
3
  import React, { useRef, useState, useEffect, forwardRef, useMemo, useId, useLayoutEffect } from 'react';
4
4
  import { DateInput as DateInput$1 } from '@mantine/dates';
5
5
  import { ToastContainer as ToastContainer$1 } from 'react-toastify';
@@ -44,6 +44,44 @@ function __rest(s, e) {
44
44
  return t;
45
45
  }
46
46
 
47
+ function __awaiter(thisArg, _arguments, P, generator) {
48
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
49
+ return new (P || (P = Promise))(function (resolve, reject) {
50
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
51
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
52
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
53
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
54
+ });
55
+ }
56
+
57
+ function __generator(thisArg, body) {
58
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
59
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
60
+ function verb(n) { return function (v) { return step([n, v]); }; }
61
+ function step(op) {
62
+ if (f) throw new TypeError("Generator is already executing.");
63
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
64
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
65
+ if (y = 0, t) op = [op[0] & 2, t.value];
66
+ switch (op[0]) {
67
+ case 0: case 1: t = op; break;
68
+ case 4: _.label++; return { value: op[1], done: false };
69
+ case 5: _.label++; y = op[1]; op = [0]; continue;
70
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
71
+ default:
72
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
73
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
74
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
75
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
76
+ if (t[2]) _.ops.pop();
77
+ _.trys.pop(); continue;
78
+ }
79
+ op = body.call(thisArg, _);
80
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
81
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : undefined, done: true };
82
+ }
83
+ }
84
+
47
85
  function __spreadArray(to, from, pack) {
48
86
  if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
49
87
  if (ar || !(i in from)) {
@@ -183,7 +221,7 @@ var Animation = function (_a) {
183
221
  var refVector = useRef(null);
184
222
  var _b = useState(null), lottie = _b[0], setLottie = _b[1];
185
223
  useEffect(function () {
186
- import('./lottie-B049-AA6.js').then(function (n) { return n.l; }).then(function (Lottie) { return setLottie(Lottie.default); });
224
+ import('./lottie-Dk1oKm_F.js').then(function (n) { return n.l; }).then(function (Lottie) { return setLottie(Lottie.default); });
187
225
  }, []);
188
226
  useEffect(function () {
189
227
  if (lottie && refVector.current) {
@@ -203,6 +241,11 @@ Animation.displayName = 'Animation';
203
241
 
204
242
  var styles$q = {"dropdown":"Dropdown-module_dropdown__Pf3Aj","option":"Dropdown-module_option__5-7L7"};
205
243
 
244
+ var Loader = function (props) {
245
+ return jsx(Loader$1, __assign({ color: "var(--color-primary-300)" }, props));
246
+ };
247
+ Loader.displayName = 'Loader';
248
+
206
249
  /**
207
250
  *
208
251
  * Dropdown component
@@ -215,6 +258,9 @@ var styles$q = {"dropdown":"Dropdown-module_dropdown__Pf3Aj","option":"Dropdown-
215
258
  * @param storeOptions - useCombobox options
216
259
  * @param maxHeight - max height of dropdown
217
260
  * @param closeOnSelect - close dropdown on option select
261
+ * @param dropdownRef - ref to the scrollable options container (e.g. to detect scroll end)
262
+ * @param onOptionsScroll - called when the options container scrolls (e.g. to load more)
263
+ * @param loadingOptions - show a loader below the options (e.g. while loading the next page)
218
264
  * @param rest - rest of the props to pass to Combobox component
219
265
  *
220
266
  * @return Attaches dropdown to target element and displays options
@@ -229,7 +275,7 @@ var styles$q = {"dropdown":"Dropdown-module_dropdown__Pf3Aj","option":"Dropdown-
229
275
  *
230
276
  */
231
277
  var Dropdown = function (_a) {
232
- var options = _a.options, children = _a.children, footer = _a.footer, storeOptions = _a.storeOptions, onOptionSubmit = _a.onOptionSubmit, maxHeight = _a.maxHeight, _b = _a.closeOnSelect, closeOnSelect = _b === undefined ? false : _b, rest = __rest(_a, ["options", "children", "footer", "storeOptions", "onOptionSubmit", "maxHeight", "closeOnSelect"]);
278
+ var options = _a.options, children = _a.children, footer = _a.footer, storeOptions = _a.storeOptions, onOptionSubmit = _a.onOptionSubmit, maxHeight = _a.maxHeight, _b = _a.closeOnSelect, closeOnSelect = _b === undefined ? false : _b, dropdownRef = _a.dropdownRef, onOptionsScroll = _a.onOptionsScroll, _c = _a.loadingOptions, loadingOptions = _c === undefined ? false : _c, _d = _a.positionDependencies, positionDependencies = _d === undefined ? [] : _d, rest = __rest(_a, ["options", "children", "footer", "storeOptions", "onOptionSubmit", "maxHeight", "closeOnSelect", "dropdownRef", "onOptionsScroll", "loadingOptions", "positionDependencies"]);
233
279
  var store = useCombobox(__assign({}, storeOptions));
234
280
  var renderOptions = function (options, onOptionSubmit) {
235
281
  if (!Array.isArray(options)) {
@@ -249,7 +295,7 @@ var Dropdown = function (_a) {
249
295
  return (jsxs(Combobox, __assign({ store: store, onOptionSubmit: function (value) {
250
296
  onOptionSubmit === null || onOptionSubmit === undefined ? undefined : onOptionSubmit(value);
251
297
  closeOnSelect && store.closeDropdown();
252
- } }, rest, { children: [jsx(Combobox.Target, { children: jsx(Flex, { w: "100%", children: typeof children === 'function' ? children(store) : children }) }), jsxs(Combobox.Dropdown, { classNames: { dropdown: styles$q.dropdown }, children: [jsx(Combobox.Options, { mah: maxHeight, style: { overflow: maxHeight ? 'auto' : 'unset' }, children: renderOptions(options, onOptionSubmit) }), footer && (jsx(Combobox.Footer, { children: typeof footer === 'function' ? footer(store) : footer }))] })] })));
298
+ }, positionDependencies: positionDependencies }, rest, { children: [jsx(Combobox.Target, { children: jsx(Flex, { w: "100%", children: typeof children === 'function' ? children(store) : children }) }), jsxs(Combobox.Dropdown, { classNames: { dropdown: styles$q.dropdown }, children: [jsxs(Combobox.Options, { ref: dropdownRef, onScroll: onOptionsScroll, mah: maxHeight, style: { overflow: maxHeight ? 'auto' : 'unset' }, children: [renderOptions(options, onOptionSubmit), loadingOptions && (jsx(Flex, { justify: "center", align: "center", py: 8, children: jsx(Loader, { size: "sm" }) }))] }), footer && (jsx(Combobox.Footer, { children: typeof footer === 'function' ? footer(store) : footer }))] })] })));
253
299
  };
254
300
  Dropdown.displayName = 'Dropdown';
255
301
  Dropdown.Option = Combobox.Option;
@@ -467,11 +513,6 @@ var Input = function (_a) {
467
513
  };
468
514
  Input.displayName = 'Input';
469
515
 
470
- var Loader = function (props) {
471
- return jsx(Loader$1, __assign({ color: "var(--color-primary-300)" }, props));
472
- };
473
- Loader.displayName = 'Loader';
474
-
475
516
  /**
476
517
  *
477
518
  * Autocomplete component
@@ -1127,10 +1168,10 @@ var Link = function (_a) {
1127
1168
  };
1128
1169
  Link.displayName = 'Link';
1129
1170
 
1130
- var styles$c = {"modal":"Modal-module_modal__gMPgi","body":"Modal-module_body__OhLb0","header":"Modal-module_header__5sBzU"};
1171
+ var styles$c = {"modal":"Modal-module_modal__gMPgi","body":"Modal-module_body__OhLb0","body--sm":"Modal-module_body--sm__PDhpT","body--md":"Modal-module_body--md__n3n3a","header":"Modal-module_header__5sBzU","actions":"Modal-module_actions__SzSYl","root":"Modal-module_root__8jbgq","inner":"Modal-module_inner__hm2ft","slide-up":"Modal-module_slide-up__mghqt"};
1131
1172
 
1132
1173
  var Modal = function (_a) {
1133
- var isOpen = _a.isOpen, onClose = _a.onClose, noLabel = _a.noLabel, yesLabel = _a.yesLabel, invertColors = _a.invertColors, underButton = _a.underButton, no = _a.no, yes = _a.yes, _b = _a.size, size = _b === undefined ? 'sm' : _b, _c = _a.padding, padding = _c === undefined ? 'sm' : _c, _d = _a.customRadius, customRadius = _d === undefined ? 16 : _d, children = _a.children, _e = _a.disabled, disabled = _e === undefined ? false : _e;
1174
+ var isOpen = _a.isOpen, onClose = _a.onClose, noLabel = _a.noLabel, yesLabel = _a.yesLabel, invertColors = _a.invertColors, underButton = _a.underButton, no = _a.no, yes = _a.yes, _b = _a.size, size = _b === undefined ? 'sm' : _b, _c = _a.padding, padding = _c === undefined ? 'sm' : _c, _d = _a.customRadius, customRadius = _d === undefined ? 16 : _d, children = _a.children, _e = _a.disabled, disabled = _e === undefined ? false : _e, ariaLabel = _a.ariaLabel, _f = _a.closeButtonAriaLabel, closeButtonAriaLabel = _f === undefined ? 'Close dialog' : _f;
1134
1175
  var handleClose = function () {
1135
1176
  if (onClose) {
1136
1177
  onClose();
@@ -1143,23 +1184,13 @@ var Modal = function (_a) {
1143
1184
  xl: '900px',
1144
1185
  full: '100%',
1145
1186
  };
1146
- var paddingMap = {
1147
- sm: '4px 24px 32px 24px',
1148
- md: '4px 64px 64px 64px',
1149
- };
1150
- return (jsxs(Modal$1, { opened: isOpen, onClose: handleClose, withCloseButton: onClose !== undefined, size: sizeMap[size], fullScreen: size === 'full', radius: customRadius, closeButtonProps: {
1151
- 'aria-label': 'Close modal',
1152
- title: 'Close modal',
1153
- icon: jsx(Close, { fill: "var(--color-neutral-200)" }),
1154
- }, classNames: {
1187
+ return (jsxs(Modal$1.Root, { opened: isOpen, onClose: handleClose, size: sizeMap[size], fullScreen: size === 'full', radius: customRadius, classNames: {
1188
+ root: styles$c.root,
1189
+ inner: styles$c.inner,
1155
1190
  content: styles$c.modal,
1156
- body: styles$c.body,
1191
+ body: classNames(styles$c.body, styles$c["body--".concat(padding)]),
1157
1192
  header: styles$c.header,
1158
- }, styles: {
1159
- body: {
1160
- padding: paddingMap[padding],
1161
- },
1162
- }, centered: true, children: [children, jsxs(Flex, { direction: 'column', gap: 16, w: 312, children: [noLabel && (jsx(Button, { disabled: disabled, variant: invertColors ? 'filled' : 'outlined', onClick: no, label: noLabel })), yesLabel && (jsx(Button, { disabled: disabled, variant: invertColors ? 'outlined' : 'filled', onClick: yes, label: yesLabel }))] }), underButton] }));
1193
+ }, centered: true, children: [jsx(Modal$1.Overlay, {}), jsxs(Modal$1.Content, { radius: customRadius, "aria-label": ariaLabel, children: [onClose !== undefined && (jsx(Modal$1.Header, { children: jsx(Modal$1.CloseButton, { "aria-label": closeButtonAriaLabel, title: closeButtonAriaLabel, icon: jsx(Close, { fill: "var(--color-neutral-200)" }) }) })), jsxs(Modal$1.Body, { children: [children, jsxs(Flex, { className: styles$c.actions, children: [noLabel && (jsx(Button, { disabled: disabled, variant: invertColors ? 'filled' : 'outlined', onClick: no, label: noLabel })), yesLabel && (jsx(Button, { disabled: disabled, variant: invertColors ? 'outlined' : 'filled', onClick: yes, label: yesLabel }))] }), underButton] })] })] }));
1163
1194
  };
1164
1195
  Modal.displayName = 'Modal';
1165
1196
 
@@ -1551,6 +1582,382 @@ var NavBarSide = function (_a) {
1551
1582
  };
1552
1583
  NavBarSide.displayName = 'NavBarSide';
1553
1584
 
1585
+ var optionPickerStyles = {"input":"OptionPicker-module_input__ODn4n","inputFocused":"OptionPicker-module_inputFocused__g-UfG","disabled":"OptionPicker-module_disabled__pSxSQ","pill":"OptionPicker-module_pill__9nUrh","pillText":"OptionPicker-module_pillText__R3FyC","pillsAndSearch":"OptionPicker-module_pillsAndSearch__E0cfZ","measurer":"OptionPicker-module_measurer__gVU50","searchInput":"OptionPicker-module_searchInput__2ox0d","option":"OptionPicker-module_option__ME1j1","optionLabel":"OptionPicker-module_optionLabel__iORv0","floatingLabel":"OptionPicker-module_floatingLabel__Z6Laa","label":"OptionPicker-module_label__7RYWe","labelTooltip":"OptionPicker-module_labelTooltip__jLwn4","statusOption":"OptionPicker-module_statusOption__CReh3"};
1586
+
1587
+ var SELECT_ALL_VALUE = '__select_all__';
1588
+ /**
1589
+ * OptionPicker component, use it to select multiple options from a list of options
1590
+ * For selecting a single option, use the Select component
1591
+ *
1592
+ * @param {OptionPickerOptionsType[]} data - The data to display in the select
1593
+ * @param {(options: OptionPickerOptionsType[]) => void} onChange - The function to call every time an option, or Select All, is clicked
1594
+ * @param {(searchTerm: string) => void} onSearch - Called when the search term changes. When provided, the parent filters `data` (e.g. on the server) and no local filtering is applied
1595
+ * @param {string} selectAllLabel - The label of the Select All option
1596
+ * @param {string} label - The label of the select
1597
+ * @param {string} placeholder - The placeholder of the search input, defaults to "Search"
1598
+ * @param {string} error - The error message to display
1599
+ * @param {string} description - The description to display
1600
+ * @param {boolean} disabled - Whether the select is disabled
1601
+ * @param {number} maxPills - The maximum number of pills to display (on top of maxLines), the rest will be displayed as a single pill with the number of remaining items
1602
+ * @param {number} maxLines - The maximum number of lines of pills, defaults to 2. The rest are shown as a single pill with the count
1603
+ * @param {() => Promise<OptionPickerOptionsType[]>} onSelectAll - With pagination (hasMore), called by Select All to get every option (matching the current search, if any), since only the loaded pages are known. If it rejects, the selection is left unchanged
1604
+ *
1605
+ * @returns {React.ReactElement} The OptionPicker component
1606
+ *
1607
+ */
1608
+ var OptionPicker = function (_a) {
1609
+ var data = _a.data, _b = _a.defaultValue, defaultValue = _b === undefined ? [] : _b, description = _a.description, disabled = _a.disabled, error = _a.error, extraOption = _a.extraOption, label = _a.label, maxPills = _a.maxPills, maxLines = _a.maxLines, onBlur = _a.onBlur, onChange = _a.onChange, onSearch = _a.onSearch, _c = _a.placeholder, placeholder = _c === undefined ? 'Search' : _c, _d = _a.selectAllLabel, selectAllLabel = _d === undefined ? 'Select All' : _d, singleValue = _a.singleValue, value = _a.value, _e = _a.hasMore, hasMore = _e === undefined ? false : _e, onLoadMore = _a.onLoadMore, _f = _a.isLoadingNextPage, isLoadingNextPage = _f === undefined ? false : _f, _g = _a.isLoading, isLoading = _g === undefined ? false : _g, onSelectAll = _a.onSelectAll;
1610
+ var isControlled = value !== undefined;
1611
+ var _h = useState(function () { var _a; return (_a = value !== null && value !== undefined ? value : defaultValue) !== null && _a !== undefined ? _a : []; }), internalValue = _h[0], _setInternalValue = _h[1];
1612
+ var _j = useState(''), searchTerm = _j[0], setSearchTerm = _j[1];
1613
+ var _k = useState(false), isFocused = _k[0], setIsFocused = _k[1];
1614
+ var selected = isControlled ? value : internalValue;
1615
+ // Latest selection, read after awaiting onSelectAll so changes made
1616
+ // while it loads aren't lost
1617
+ var selectedRef = useRef(selected);
1618
+ selectedRef.current = selected;
1619
+ var _l = useState(false), isSelectingAll = _l[0], setIsSelectingAll = _l[1];
1620
+ var hasSelectedOptions = selected.length > 0;
1621
+ var dropdownContentRef = useRef(null);
1622
+ // Set when onLoadMore is called, so scroll events don't call it again
1623
+ // before the parent responds (with new data or isLoadingNextPage)
1624
+ var loadMoreRequestedRef = useRef(false);
1625
+ /**
1626
+ * Loads the next page when the options list is scrolled to the bottom,
1627
+ * or when it doesn't fill its height (so there's nothing to scroll).
1628
+ * Defined on every render, so it always calls the current onLoadMore.
1629
+ */
1630
+ var loadMoreIfNeeded = function () {
1631
+ var el = dropdownContentRef.current;
1632
+ if (!el || !hasMore || !onLoadMore || isLoadingNextPage)
1633
+ return;
1634
+ if (loadMoreRequestedRef.current)
1635
+ return;
1636
+ // The list stays mounted while closed, with no height
1637
+ if (el.clientHeight === 0)
1638
+ return;
1639
+ if (el.scrollTop + el.clientHeight + 1 >= el.scrollHeight) {
1640
+ loadMoreRequestedRef.current = true;
1641
+ onLoadMore();
1642
+ }
1643
+ };
1644
+ // New data or a finished load: allow the next request, and keep loading
1645
+ // while the list still doesn't fill its height
1646
+ useEffect(function () {
1647
+ loadMoreRequestedRef.current = false;
1648
+ loadMoreIfNeeded();
1649
+ }, [data, hasMore, isLoadingNextPage]);
1650
+ var setInternalValue = function (newValue) {
1651
+ if (isControlled) {
1652
+ return;
1653
+ }
1654
+ _setInternalValue(newValue);
1655
+ };
1656
+ var updateSelected = function (newSelected) {
1657
+ setInternalValue(newSelected);
1658
+ onChange === null || onChange === undefined ? undefined : onChange(newSelected);
1659
+ };
1660
+ /* -----------------------------------
1661
+ * Handlers
1662
+ * ----------------------------------- */
1663
+ var handleBlur = function (e) {
1664
+ // Focus moving into the search input (or anything else inside) isn't a blur
1665
+ if (e.currentTarget.contains(e.relatedTarget)) {
1666
+ return;
1667
+ }
1668
+ setIsFocused(false);
1669
+ onBlur === null || onBlur === undefined ? undefined : onBlur();
1670
+ };
1671
+ var inputRef = useRef(null);
1672
+ /**
1673
+ * Closing the dropdown with Escape removes the focused search input, and
1674
+ * browsers don't fire a blur for that, so focus is lost silently.
1675
+ * Return it to the input so the next Tab or click outside fires onBlur.
1676
+ * Only for keyboard closes: a click outside is the user leaving the field.
1677
+ */
1678
+ var handleDropdownClose = function (eventSource) {
1679
+ if (eventSource !== 'keyboard') {
1680
+ return;
1681
+ }
1682
+ requestAnimationFrame(function () {
1683
+ var _a;
1684
+ var active = document.activeElement;
1685
+ if (!active || active === document.body) {
1686
+ (_a = inputRef.current) === null || _a === undefined ? undefined : _a.focus();
1687
+ }
1688
+ });
1689
+ };
1690
+ var handleSearch = function (term) {
1691
+ setSearchTerm(term);
1692
+ onSearch === null || onSearch === undefined ? undefined : onSearch(term);
1693
+ };
1694
+ /** Toggle a single option */
1695
+ var handleToggle = function (option) {
1696
+ var newSelected = selected.some(function (o) { return o.value === option.value; })
1697
+ ? selected.filter(function (o) { return o.value !== option.value; })
1698
+ : __spreadArray(__spreadArray([], selected, true), [option], false);
1699
+ if (singleValue) {
1700
+ newSelected = newSelected.slice(-1);
1701
+ }
1702
+ updateSelected(newSelected);
1703
+ // The user found what they searched for, so clear the search
1704
+ if (searchTerm) {
1705
+ handleSearch('');
1706
+ }
1707
+ };
1708
+ /** Toggle every visible, enabled option at once */
1709
+ var handleSelectAll = function () { return __awaiter(undefined, undefined, undefined, function () {
1710
+ var selectableOptions, isSelected, allSelected, allOptions, current_1, newSelected;
1711
+ return __generator(this, function (_b) {
1712
+ switch (_b.label) {
1713
+ case 0:
1714
+ selectableOptions = filteredOptions.filter(function (o) { return !o.disabled; });
1715
+ isSelected = function (option) {
1716
+ return selected.some(function (o) { return o.value === option.value; });
1717
+ };
1718
+ allSelected = selectableOptions.every(isSelected);
1719
+ if (!(hasMore && onSelectAll)) return [3 /*break*/, 6];
1720
+ if (allSelected) {
1721
+ handleClearAll();
1722
+ return [2 /*return*/];
1723
+ }
1724
+ setIsSelectingAll(true);
1725
+ _b.label = 1;
1726
+ case 1:
1727
+ _b.trys.push([1, 3, 4, 5]);
1728
+ return [4 /*yield*/, onSelectAll()];
1729
+ case 2:
1730
+ allOptions = _b.sent();
1731
+ current_1 = selectedRef.current;
1732
+ updateSelected(__spreadArray(__spreadArray([], current_1, true), allOptions.filter(function (option) {
1733
+ return !option.disabled && !current_1.some(function (o) { return o.value === option.value; });
1734
+ }), true));
1735
+ return [3 /*break*/, 5];
1736
+ case 3:
1737
+ _b.sent();
1738
+ return [3 /*break*/, 5];
1739
+ case 4:
1740
+ setIsSelectingAll(false);
1741
+ return [7 /*endfinally*/];
1742
+ case 5: return [2 /*return*/];
1743
+ case 6:
1744
+ newSelected = allSelected
1745
+ ? selected.filter(function (o) { return !selectableOptions.some(function (option) { return option.value === o.value; }); })
1746
+ : __spreadArray(__spreadArray([], selected, true), selectableOptions.filter(function (option) { return !isSelected(option); }), true);
1747
+ updateSelected(newSelected);
1748
+ return [2 /*return*/];
1749
+ }
1750
+ });
1751
+ }); };
1752
+ /** Clicking the "X" on a pill => remove it immediately */
1753
+ var handleDeselectPill = function (optionId) {
1754
+ updateSelected(selected.filter(function (o) { return o.value !== optionId; }));
1755
+ };
1756
+ /** Clicking the "X" on the "Select All" pill => keep only disabled options */
1757
+ var handleClearAll = function () {
1758
+ updateSelected(selected.filter(function (o) { return o.disabled; }));
1759
+ };
1760
+ // Every option is loaded and selected => show a single "Select All" pill.
1761
+ // Only when `data` is known to be the full list: with onSearch it may hold
1762
+ // the results of a search (even right after the term is cleared, until the
1763
+ // parent responds), and with hasMore some pages aren't loaded yet.
1764
+ // Never in singleValue mode, which has no Select All
1765
+ var enabledDataOptions = data.filter(function (o) { return !o.disabled; });
1766
+ var allOptionsSelected = !singleValue &&
1767
+ !onSearch &&
1768
+ !hasMore &&
1769
+ enabledDataOptions.length > 0 &&
1770
+ enabledDataOptions.every(function (option) {
1771
+ return selected.some(function (o) { return o.value === option.value; });
1772
+ });
1773
+ /* -----------------------------------
1774
+ * Search / Filtering
1775
+ * ----------------------------------- */
1776
+ // With onSearch the parent filters (e.g. on the server, by fields other than
1777
+ // the label), so `data` already holds the results and is shown as is
1778
+ var filteredOptions = useMemo(function () {
1779
+ if (onSearch || !searchTerm.trim()) {
1780
+ return data;
1781
+ }
1782
+ return data.filter(function (o) {
1783
+ return o.label.toLowerCase().includes(searchTerm.toLowerCase());
1784
+ });
1785
+ }, [searchTerm, data, onSearch]);
1786
+ // Build the actual dropdown checkboxes from the filtered list.
1787
+ // Not memoized on purpose: the handlers close over onChange and the current
1788
+ // selection, so a memo would keep calling a stale onChange.
1789
+ var renderDropdownOptions = function () {
1790
+ var selectableOptions = filteredOptions.filter(function (o) { return !o.disabled; });
1791
+ var allSelected = selectableOptions.length > 0 &&
1792
+ selectableOptions.every(function (option) {
1793
+ return selected.some(function (o) { return o.value === option.value; });
1794
+ });
1795
+ var selectAllOption = !singleValue && selectableOptions.length > 0 ? (jsxs(React.Fragment, { children: [jsx(Option, { checked: allSelected, onChange: handleSelectAll, option: {
1796
+ value: SELECT_ALL_VALUE,
1797
+ label: selectAllLabel,
1798
+ // Blocked, with a loader, while onSelectAll is loading
1799
+ disabled: isSelectingAll,
1800
+ labelIcon: isSelectingAll ? jsx(Loader, { size: "xs" }) : undefined,
1801
+ } }), jsx(Divider, {})] }, "select_all")) : null;
1802
+ var fixedOption = extraOption ? (jsxs(React.Fragment, { children: [jsx(Option, { checked: (extraOption.disabled && extraOption.selected) ||
1803
+ selected.some(function (o) { return o.value === extraOption.value; }), onChange: handleToggle, option: extraOption }), jsx(Divider, {})] }, "extra_option")) : null;
1804
+ // Now build the rest of the normal checkboxes
1805
+ var normalOptions = filteredOptions.map(function (option) { return (jsx(Option, { checked: (option.disabled && option.selected) ||
1806
+ selected.some(function (o) { return o.value === option.value; }), onChange: handleToggle, option: option }, option.value)); });
1807
+ var loadingOption = (jsx(Dropdown.Option, { value: "Loading...", disabled: true, className: optionPickerStyles.statusOption, children: jsx(Flex, { justify: "center", align: "center", py: 8, children: jsx(Loader, { size: "sm" }) }) }, "loading"));
1808
+ var emptyOption = (jsx(Dropdown.Option, { value: "No options found", disabled: true, className: optionPickerStyles.statusOption, children: "No matches found, try something else." }, "no_options"));
1809
+ if (isLoading) {
1810
+ return fixedOption ? [fixedOption, loadingOption] : [loadingOption];
1811
+ }
1812
+ var options = normalOptions.length > 0
1813
+ ? selectAllOption
1814
+ ? __spreadArray([selectAllOption], normalOptions, true) : normalOptions
1815
+ : [emptyOption];
1816
+ return fixedOption ? __spreadArray([fixedOption], options, true) : options;
1817
+ };
1818
+ return (jsx(Dropdown, { options: renderDropdownOptions(), maxHeight: 300, dropdownRef: dropdownContentRef, onOptionsScroll: loadMoreIfNeeded, loadingOptions: isLoadingNextPage,
1819
+ // Pills change the input height, so the dropdown needs repositioning
1820
+ positionDependencies: [hasSelectedOptions], disabled: disabled, storeOptions: {
1821
+ onDropdownClose: handleDropdownClose,
1822
+ // Wait for the list to be visible before checking if it needs more
1823
+ onDropdownOpen: function () { return requestAnimationFrame(loadMoreIfNeeded); },
1824
+ }, children: function (store) {
1825
+ var _a, _b;
1826
+ var floatLabel = store.dropdownOpened || hasSelectedOptions;
1827
+ return (jsxs("div", { className: styles$p.container, children: [floatLabel && (jsx(FloatingLabel, { label: label, className: classNames((_a = {},
1828
+ _a[styles$p.labelError] = error && !isFocused,
1829
+ _a[styles$p.labelFocused] = isFocused,
1830
+ _a)), onClick: function () { return !disabled && store.toggleDropdown(); } })), jsxs(Box, { ref: inputRef, className: classNames(styles$p.input, optionPickerStyles.input, (_b = {},
1831
+ _b[styles$p.inputError] = error,
1832
+ _b[optionPickerStyles.inputFocused] = store.dropdownOpened,
1833
+ _b[optionPickerStyles.disabled] = disabled,
1834
+ _b)), onClick: function () { return !disabled && store.toggleDropdown(); }, onFocus: function () { return setIsFocused(true); }, onBlur: handleBlur, tabIndex: disabled ? -1 : 0, children: [jsx(PillAndSearchSection, { allSelected: allOptionsSelected, clearAll: handleClearAll, selectAllLabel: selectAllLabel, label: label, placeholder: placeholder, searchTerm: searchTerm, onSearch: handleSearch, value: selected, removeOption: handleDeselectPill, store: store, maxPills: maxPills, maxLines: maxLines }), jsx(Chevron, { rotate: store.dropdownOpened ? 90 : 270 })] }), error ? (jsx(ErrorSection, { error: error })) : description ? (jsx(DescriptionSection, { description: description })) : null] }));
1835
+ } }));
1836
+ };
1837
+ /**
1838
+ * The label floating over the input border, cut with an ellipsis when too long.
1839
+ * Only when it's cut, hovering it shows the full label in a tooltip.
1840
+ */
1841
+ var FloatingLabel = function (_a) {
1842
+ var label = _a.label, className = _a.className, onClick = _a.onClick;
1843
+ var labelRef = useRef(null);
1844
+ var _b = useState(false), isTruncated = _b[0], setIsTruncated = _b[1];
1845
+ var checkTruncated = function () {
1846
+ var el = labelRef.current;
1847
+ setIsTruncated(!!el && el.scrollWidth > el.clientWidth);
1848
+ };
1849
+ return (jsx(Tooltip, { label: label, position: "top-start", disabled: !isTruncated, className: optionPickerStyles.labelTooltip, children: jsx("label", { ref: labelRef, className: classNames(styles$p.label, optionPickerStyles.floatingLabel, className), onMouseEnter: checkTruncated, onClick: onClick, children: label }) }));
1850
+ };
1851
+ var ErrorSection = function (_a) {
1852
+ var error = _a.error;
1853
+ return (jsxs(Flex, { className: styles$p.error, gap: 4, ml: 16, mt: 8, style: { visibility: error ? 'visible' : 'hidden' }, children: [jsx(Info, { width: "18px", height: "18px", viewBox: "0 0 24 24", stroke: "var(--color-red-300)" }), jsx(Text, { variant: "xs", className: classNames(styles$p.errorText), children: error })] }));
1854
+ };
1855
+ var DescriptionSection = function (_a) {
1856
+ var description = _a.description;
1857
+ return (jsx(Flex, { className: styles$p.description, gap: 4, ml: 16, mt: 8, style: { visibility: description ? 'visible' : 'hidden' }, children: jsx(Text, { variant: "xs", children: description }) }));
1858
+ };
1859
+ var Option = function (_a) {
1860
+ var checked = _a.checked, onChange = _a.onChange, option = _a.option;
1861
+ return (jsx(Dropdown.Option, { value: option.value, py: 12, disabled: option.disabled, className: optionPickerStyles.option,
1862
+ // Toggle from anywhere in the row, and don't close on click
1863
+ onClick: function (e) {
1864
+ e.stopPropagation();
1865
+ if (!option.disabled) {
1866
+ onChange(option);
1867
+ }
1868
+ }, children: jsx(Group, { children: jsxs("div", { className: optionPickerStyles.optionLabel, children: [jsx(Checkbox, { label: jsxs(Flex, { gap: 4, children: [option.label, option.labelIcon ? jsx(Fragment, { children: option.labelIcon }) : null] }), checked: checked, disabled: option.disabled, readOnly: true, tabIndex: -1, flex: 1 }, option.value), option.suffix && jsx(Text, { as: "span", children: option.suffix })] }) }) }));
1869
+ };
1870
+ var PILLS_GAP = 8;
1871
+ var SEARCH_INPUT_MIN_WIDTH = 40;
1872
+ /**
1873
+ * Simulates the flex-wrap layout to check whether the given widths fit
1874
+ * in `maxLines` lines of `containerWidth`
1875
+ */
1876
+ var fitsInLines = function (widths, containerWidth, maxLines) {
1877
+ var lines = 1;
1878
+ var lineWidth = 0;
1879
+ for (var _i = 0, widths_1 = widths; _i < widths_1.length; _i++) {
1880
+ var width = widths_1[_i];
1881
+ if (lineWidth === 0) {
1882
+ lineWidth = width;
1883
+ }
1884
+ else if (lineWidth + PILLS_GAP + width <= containerWidth) {
1885
+ lineWidth += PILLS_GAP + width;
1886
+ }
1887
+ else {
1888
+ lines++;
1889
+ lineWidth = width;
1890
+ }
1891
+ }
1892
+ return lines <= maxLines;
1893
+ };
1894
+ var Pill$1 = function (_a) {
1895
+ var label = _a.label, onRemove = _a.onRemove, _b = _a.removeAriaLabel, removeAriaLabel = _b === undefined ? "Remove ".concat(label) : _b;
1896
+ return (jsxs("span", { className: optionPickerStyles.pill, title: label, "data-pill": true, children: [jsx(Text, { variant: "xs", className: optionPickerStyles.pillText, children: label }), jsx(ActionIcon$1, { variant: "transparent", "aria-label": removeAriaLabel, onClick: function (e) {
1897
+ e.stopPropagation();
1898
+ onRemove();
1899
+ }, children: jsx(Close, { height: "20px", width: "20px", stroke: "var(--color-neutral-600)" }) })] }));
1900
+ };
1901
+ var PillAndSearchSection = function (_a) {
1902
+ var allSelected = _a.allSelected, clearAll = _a.clearAll, label = _a.label, _b = _a.maxPills, maxPills = _b === undefined ? Infinity : _b, _c = _a.maxLines, maxLines = _c === undefined ? 2 : _c, selectAllLabel = _a.selectAllLabel, onSearch = _a.onSearch, placeholder = _a.placeholder, searchTerm = _a.searchTerm, removeOption = _a.removeOption, store = _a.store, _d = _a.value, value = _d === undefined ? [] : _d;
1903
+ var dropdownOpened = store.dropdownOpened;
1904
+ var searchInputRef = useRef(null);
1905
+ var measurerRef = useRef(null);
1906
+ useEffect(function () {
1907
+ var _a;
1908
+ if (dropdownOpened) {
1909
+ (_a = searchInputRef.current) === null || _a === undefined ? undefined : _a.focus();
1910
+ }
1911
+ }, [dropdownOpened]);
1912
+ var enabledOptions = value.filter(function (option) { return !option.disabled; });
1913
+ var enabledKey = enabledOptions.map(function (o) { return o.value; }).join('_');
1914
+ var _e = useState(0), containerWidth = _e[0], setContainerWidth = _e[1];
1915
+ var _f = useState(enabledOptions.length), visibleCount = _f[0], setVisibleCount = _f[1];
1916
+ // Track the available width, the pills are re-measured when it changes
1917
+ useEffect(function () {
1918
+ var el = measurerRef.current;
1919
+ if (!el)
1920
+ return;
1921
+ var observer = new ResizeObserver(function () { return setContainerWidth(el.clientWidth); });
1922
+ observer.observe(el);
1923
+ return function () { return observer.disconnect(); };
1924
+ }, [allSelected]);
1925
+ // Show as many pills as fit in `maxLines`, leaving room for the "+N" pill
1926
+ // and the search input (when open)
1927
+ useLayoutEffect(function () {
1928
+ var _a, _b;
1929
+ var el = measurerRef.current;
1930
+ if (!el || !containerWidth)
1931
+ return;
1932
+ var pillWidths = Array.from(el.querySelectorAll('[data-pill]'), function (pill) { return pill.offsetWidth; });
1933
+ var moreWidth = (_b = (_a = el.querySelector('[data-more]')) === null || _a === undefined ? undefined : _a.offsetWidth) !== null && _b !== undefined ? _b : 0;
1934
+ var total = pillWidths.length;
1935
+ var fits = function (count) {
1936
+ return fitsInLines(__spreadArray(__spreadArray(__spreadArray([], pillWidths.slice(0, count), true), (count < total ? [moreWidth] : []), true), (dropdownOpened ? [SEARCH_INPUT_MIN_WIDTH] : []), true), containerWidth, maxLines);
1937
+ };
1938
+ var count = Math.min(total, maxPills);
1939
+ while (count > 0 && !fits(count)) {
1940
+ count--;
1941
+ }
1942
+ setVisibleCount(count);
1943
+ }, [enabledKey, containerWidth, dropdownOpened, maxLines, maxPills]);
1944
+ var searchInput = dropdownOpened && (jsx("input", { ref: searchInputRef, type: "text", placeholder: placeholder, "aria-label": placeholder, value: searchTerm, onChange: function (e) { return onSearch(e.target.value); }, onClick: function (e) { return e.stopPropagation(); }, className: optionPickerStyles.searchInput }));
1945
+ // Everything is selected => a single "Select All" pill instead of one per option
1946
+ if (allSelected) {
1947
+ return (jsxs(Flex, { gap: PILLS_GAP, align: "center", wrap: "wrap", className: optionPickerStyles.pillsAndSearch, children: [jsx(Pill$1, { label: selectAllLabel, onRemove: clearAll, removeAriaLabel: "Clear all" }), searchInput] }));
1948
+ }
1949
+ var visibleOptions = enabledOptions.slice(0, visibleCount);
1950
+ var hiddenOptions = enabledOptions.slice(visibleCount);
1951
+ return (jsxs(Flex, { gap: PILLS_GAP, align: "center", wrap: "wrap", className: optionPickerStyles.pillsAndSearch, children: [jsxs("div", { ref: measurerRef, className: optionPickerStyles.measurer, "aria-hidden": true, children: [enabledOptions.map(function (option) { return (jsx(Pill$1, { label: option.label, onRemove: function () { } }, option.value)); }), jsx("span", { className: optionPickerStyles.pill, "data-more": true, children: jsxs(Text, { variant: "xs", children: ["+", enabledOptions.length] }) })] }), enabledOptions.length === 0 && !dropdownOpened && (jsx(Text, { variant: "s", className: optionPickerStyles.label, title: label, children: label })), visibleOptions.map(function (option) { return (jsx(Pill$1, { label: option.label, onRemove: function () { return removeOption(option.value); } }, option.value)); }), hiddenOptions.length > 0 && (jsx(Tooltip, { label: hiddenOptions.map(function (o) { return o.label; }).join(', '), position: "bottom", children: jsx("span", { className: optionPickerStyles.pill, children: jsxs(Text, { variant: "xs", children: ["+", hiddenOptions.length] }) }, "more") })), searchInput] }));
1952
+ };
1953
+ OptionPicker.displayName = 'OptionPicker';
1954
+ FloatingLabel.displayName = 'FloatingLabel';
1955
+ ErrorSection.displayName = 'ErrorSection';
1956
+ DescriptionSection.displayName = 'DescriptionSection';
1957
+ Option.displayName = 'Option';
1958
+ Pill$1.displayName = 'Pill';
1959
+ PillAndSearchSection.displayName = 'PillAndSearchSection';
1960
+
1554
1961
  var Pagination = function (_a) {
1555
1962
  var onChange = _a.onChange, value = _a.value, total = _a.total, disabled = _a.disabled;
1556
1963
  return (jsx(Pagination$1, { radius: "xl", previousIcon: function () { return jsx(Chevron, {}); }, nextIcon: function () { return jsx(Chevron, { rotate: 180 }); }, onChange: onChange, value: value, total: total, styles: { control: { border: 'none', color: 'black' } }, color: "var(--color-neutral-300)", disabled: disabled }));
@@ -2258,5 +2665,5 @@ var World = function (_a) {
2258
2665
  };
2259
2666
  World.displayName = 'World';
2260
2667
 
2261
- export { Arrow as $, Accordion as A, Badge as B, Card as C, DateInput as D, EnterCodeInput as E, FileInput as F, StatusBadge as G, Stepper as H, IncrementNumber as I, Tabs as J, Text as K, Link as L, Message as M, NavBar as N, Textarea as O, Pagination as P, ToastContainer as Q, RadioButton as R, SegmentedControl as S, Table as T, Tooltip as U, AIGenerate as V, AIPrompt as W, AIRedo as X, AISearch as Y, Alert as Z, Archive as _, AccordionItem as a, Attachment as a0, AvatarPlaceholder as a1, Bell as a2, Calendar as a3, Camera as a4, Cat as a5, Chat as a6, Check as a7, CheckSquare as a8, Chevron as a9, MenuTouched as aA, Notification as aB, Open as aC, Pets as aD, Phone as aE, Plaid as aF, Plus as aG, Refresh as aH, Remove as aI, Search as aJ, Shield as aK, StepIndicator as aL, ThreeDots as aM, ThumbsDown as aN, Tick as aO, Umbrello as aP, Upload as aQ, Visa as aR, Warning as aS, World as aT, capitalizeFirstSentenceLetter as aU, ChevronCollapse as aa, Clock as ab, Close as ac, Cloud as ad, CloudUpload as ae, CreditCard as af, Curriculum as ag, Delete as ah, Document as ai, Dog as aj, EyeHide as ak, EyeShow as al, Fingerprint as am, Flash as an, Google as ao, Grid as ap, Hamburger as aq, HappyFace as ar, House as as, HouseCheck as at, HouseTime as au, Identification as av, Info as aw, Location as ax, Mail as ay, Menu as az, AccordionControl as b, AccordionPanel as c, ActionIcon as d, Animation as e, Autocomplete as f, getDefaultExportFromCjs as g, Avatar as h, badgeVariantMap as i, Button as j, cardStatusVariantMap as k, CardStatus as l, Checkbox as m, Chip as n, Dropdown as o, FileUpload as p, Input as q, LinkModal as r, Loader as s, Modal as t, Multiselect as u, NavBarSide as v, PasswordInput as w, Pill as x, ProgressStepper as y, Select as z };
2262
- //# sourceMappingURL=index-CAIOxTa3.js.map
2668
+ export { Archive as $, Accordion as A, Badge as B, Card as C, DateInput as D, EnterCodeInput as E, FileInput as F, StatusBadge as G, Stepper as H, IncrementNumber as I, Tabs as J, Text as K, Link as L, Message as M, NavBar as N, OptionPicker as O, Pagination as P, Textarea as Q, RadioButton as R, SegmentedControl as S, Table as T, ToastContainer as U, Tooltip as V, AIGenerate as W, AIPrompt as X, AIRedo as Y, AISearch as Z, Alert as _, AccordionItem as a, Arrow as a0, Attachment as a1, AvatarPlaceholder as a2, Bell as a3, Calendar as a4, Camera as a5, Cat as a6, Chat as a7, Check as a8, CheckSquare as a9, Menu as aA, MenuTouched as aB, Notification as aC, Open as aD, Pets as aE, Phone as aF, Plaid as aG, Plus as aH, Refresh as aI, Remove as aJ, Search as aK, Shield as aL, StepIndicator as aM, ThreeDots as aN, ThumbsDown as aO, Tick as aP, Umbrello as aQ, Upload as aR, Visa as aS, Warning as aT, World as aU, capitalizeFirstSentenceLetter as aV, Chevron as aa, ChevronCollapse as ab, Clock as ac, Close as ad, Cloud as ae, CloudUpload as af, CreditCard as ag, Curriculum as ah, Delete as ai, Document as aj, Dog as ak, EyeHide as al, EyeShow as am, Fingerprint as an, Flash as ao, Google as ap, Grid as aq, Hamburger as ar, HappyFace as as, House as at, HouseCheck as au, HouseTime as av, Identification as aw, Info as ax, Location as ay, Mail as az, AccordionControl as b, AccordionPanel as c, ActionIcon as d, Animation as e, Autocomplete as f, getDefaultExportFromCjs as g, Avatar as h, badgeVariantMap as i, Button as j, cardStatusVariantMap as k, CardStatus as l, Checkbox as m, Chip as n, Dropdown as o, FileUpload as p, Input as q, LinkModal as r, Loader as s, Modal as t, Multiselect as u, NavBarSide as v, PasswordInput as w, Pill as x, ProgressStepper as y, Select as z };
2669
+ //# sourceMappingURL=index-B6PiHvyN.js.map