@agilant/toga-blox 1.0.318-beta.9 → 1.0.319-beta.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/dist/api/tableData/getDataTableData.js +10 -18
  2. package/dist/assets/Logo.png +0 -0
  3. package/dist/assets/cable.jpg +0 -0
  4. package/dist/assets/card-1.jpg +0 -0
  5. package/dist/assets/cat-logo.png +0 -0
  6. package/dist/assets/compass-card-image-2.png +0 -0
  7. package/dist/assets/compass-card-image-3.png +0 -0
  8. package/dist/assets/compass-card-image-4.png +0 -0
  9. package/dist/assets/compass-card-image.png +0 -0
  10. package/dist/assets/compass-logo.png +0 -0
  11. package/dist/assets/compass-tech-hero-bg.png +0 -0
  12. package/dist/assets/contact-image.png +0 -0
  13. package/dist/assets/green-laptop.png +0 -0
  14. package/dist/assets/heroImage.png +0 -0
  15. package/dist/assets/item.jpg +0 -0
  16. package/dist/assets/map.png +0 -0
  17. package/dist/assets/placeholder-no-image-available.png +0 -0
  18. package/dist/assets/team.png +0 -0
  19. package/dist/components/AdvancedSelect/AdvancedSelect.d.ts +3 -0
  20. package/dist/components/AdvancedSelect/AdvancedSelect.js +294 -0
  21. package/dist/components/AdvancedSelect/AdvancedSelect.module.css +448 -0
  22. package/dist/components/AdvancedSelect/AdvancedSelect.types.d.ts +74 -0
  23. package/dist/components/AdvancedSelect/index.d.ts +3 -0
  24. package/dist/components/AdvancedSelect/index.js +2 -0
  25. package/dist/components/BaseAddress/BaseAddress.module.css +1 -2
  26. package/dist/components/BaseButton/BaseButton.d.ts +4 -2
  27. package/dist/components/BaseButton/BaseButton.js +2 -0
  28. package/dist/components/BaseButton/BaseButton.module.css +112 -0
  29. package/dist/components/BaseDetailField/BaseDetailField.js +2 -2
  30. package/dist/components/BaseDetailField/BaseDetailField.module.css +3 -7
  31. package/dist/components/BaseDetailField/renderBaseDetailFieldLabel.d.ts +1 -1
  32. package/dist/components/BaseDetailField/renderBaseDetailFieldLabel.js +4 -2
  33. package/dist/components/BaseDetailField/renderBaseDetailFieldValue.js +70 -11
  34. package/dist/components/BaseDetailField/types.d.ts +1 -0
  35. package/dist/components/BaseInput/BaseCheckbox.d.ts +1 -3
  36. package/dist/components/BaseInput/BaseCheckbox.js +7 -5
  37. package/dist/components/BaseInput/BaseInput.js +42 -39
  38. package/dist/components/BaseInput/BaseInput.module.css +489 -0
  39. package/dist/components/BaseInput/BaseInput.types.d.ts +13 -6
  40. package/dist/components/BaseInput/BaseMultiSelect.d.ts +1 -15
  41. package/dist/components/BaseInput/BaseMultiSelect.js +40 -30
  42. package/dist/components/BaseInput/BaseRadio.d.ts +3 -6
  43. package/dist/components/BaseInput/BaseRadio.js +4 -2
  44. package/dist/components/BaseInput/BaseSelect.d.ts +3 -16
  45. package/dist/components/BaseInput/BaseSelect.js +20 -37
  46. package/dist/components/BaseInput/BaseTextInput.d.ts +1 -7
  47. package/dist/components/BaseInput/BaseTextInput.js +29 -21
  48. package/dist/components/BaseInput/BaseTextareaInput.d.ts +3 -13
  49. package/dist/components/BaseInput/BaseTextareaInput.js +14 -24
  50. package/dist/components/BaseInput/BaseToggle.d.ts +2 -11
  51. package/dist/components/BaseInput/BaseToggle.js +3 -3
  52. package/dist/components/BaseInput/DisabledSelect.d.ts +0 -1
  53. package/dist/components/BaseInput/DisabledSelect.js +6 -3
  54. package/dist/components/BaseInput/components/BaseErrorMessage.d.ts +1 -3
  55. package/dist/components/BaseInput/components/BaseErrorMessage.js +4 -2
  56. package/dist/components/BaseInput/components/CharacterLimitMessage.js +3 -3
  57. package/dist/components/BaseInput/components/Option.js +12 -5
  58. package/dist/components/BaseInput/components/SingleValue.js +2 -2
  59. package/dist/components/BaseToolTip/BaseToolTip.module.css +5 -2
  60. package/dist/components/BaseTotals/BaseTotals.js +8 -4
  61. package/dist/components/BaseTotals/BaseTotals.module.css +16 -4
  62. package/dist/components/BaseTotals/types.d.ts +4 -1
  63. package/dist/components/Card/templates/CategoryCardTemplate.js +2 -1
  64. package/dist/components/Card/templates/CompassCardTemplate.js +2 -1
  65. package/dist/components/Card/templates/CounterContentCardTemplate.js +2 -1
  66. package/dist/components/Card/templates/HorizontalCardTemplate.js +2 -1
  67. package/dist/components/Card/templates/ItemCardTemplate.js +2 -1
  68. package/dist/components/Card/templates/KitContentCardTemplate.js +2 -1
  69. package/dist/components/Card/templates/ShippingAddressCardTemplate.js +2 -1
  70. package/dist/components/Card/templates/VerticalCardTemplate.js +2 -1
  71. package/dist/components/DetailSection/types.d.ts +20 -1
  72. package/dist/components/EnvironmentBadge/EnvironmentBadge.css +55 -0
  73. package/dist/components/EnvironmentBadge/EnvironmentBadge.d.ts +4 -0
  74. package/dist/components/EnvironmentBadge/EnvironmentBadge.js +26 -0
  75. package/dist/components/EnvironmentBadge/index.d.ts +1 -0
  76. package/dist/components/EnvironmentBadge/index.js +1 -0
  77. package/dist/components/Footer/Footer.js +2 -1
  78. package/dist/components/Hero/Hero.js +3 -2
  79. package/dist/components/Image/Image.js +3 -5
  80. package/dist/components/Image/types.d.ts +1 -0
  81. package/dist/components/Nav/Nav.js +2 -1
  82. package/dist/components/Table/components/cellTypes/ClientCell.d.ts +5 -1
  83. package/dist/components/Table/components/cellTypes/ClientCell.js +7 -4
  84. package/dist/components/Table/components/cellTypes/CopyableCell.d.ts +2 -1
  85. package/dist/components/Table/components/cellTypes/CopyableCell.js +4 -3
  86. package/dist/components/Table/components/cellTypes/EditableCell.js +2 -2
  87. package/dist/components/Table/components/cellTypes/ImageCell.d.ts +4 -0
  88. package/dist/components/Table/components/cellTypes/ImageCell.js +4 -0
  89. package/dist/components/Table/components/cellTypes/UserAvatarCell.d.ts +5 -1
  90. package/dist/components/Table/components/cellTypes/UserAvatarCell.js +7 -4
  91. package/dist/components/Table/components/columnFiltersAndSorts/HeaderFilterDate.js +2 -2
  92. package/dist/components/Table/components/columnFiltersAndSorts/HeaderFilterMultiselect.js +1 -1
  93. package/dist/components/Table/components/columnFiltersAndSorts/HeaderFilterRange.js +1 -1
  94. package/dist/components/Table/components/columnFiltersAndSorts/HeaderFilterSearch.js +2 -2
  95. package/dist/components/Table/components/columnFiltersAndSorts/SortIcon.js +2 -2
  96. package/dist/components/Table/hooks/index.d.ts +2 -0
  97. package/dist/components/Table/hooks/index.js +2 -0
  98. package/dist/components/Table/hooks/useAssignTableFieldLabels.d.ts +16 -0
  99. package/dist/components/Table/hooks/useAssignTableFieldLabels.js +39 -0
  100. package/dist/components/Table/hooks/useTableData.d.ts +21 -0
  101. package/dist/components/Table/hooks/useTableData.js +94 -0
  102. package/dist/components/Table/index.d.ts +1 -0
  103. package/dist/components/Table/index.js +1 -0
  104. package/dist/components/Table/themeConfig/toga.module.css +125 -33
  105. package/dist/components/Table/types.d.ts +27 -0
  106. package/dist/components/Table/utils/buildActionColumn.js +1 -1
  107. package/dist/components/Table/utils/buildTanstackColumns.js +26 -6
  108. package/dist/components/Table/utils/resolveCellType.d.ts +1 -2
  109. package/dist/components/Table/utils/resolveCellType.js +20 -34
  110. package/dist/components/Table/variants.d.ts +1 -1
  111. package/dist/components/TableRecordModal/TableRecordModal.d.ts +2 -1
  112. package/dist/components/TableRecordModal/TableRecordModal.js +2 -2
  113. package/dist/components/TableRecordModal/tableRecordModal.module.css +106 -116
  114. package/dist/components/Toaster/Toaster.js +12 -8
  115. package/dist/components/Toaster/Toaster.module.css +53 -0
  116. package/dist/components/Toaster/types.d.ts +6 -2
  117. package/dist/components/ToggleButton/ToggleButton.d.ts +3 -0
  118. package/dist/components/ToggleButton/ToggleButton.js +26 -47
  119. package/dist/components/ToggleButton/ToggleButton.module.css +203 -0
  120. package/dist/components/ToggleButton/ToggleButton.types.d.ts +13 -11
  121. package/dist/components/index.d.ts +2 -3
  122. package/dist/components/index.js +2 -3
  123. package/dist/components/old/BaseInput/BaseCheckbox.d.ts +10 -0
  124. package/dist/components/old/BaseInput/BaseCheckbox.js +9 -0
  125. package/dist/components/old/BaseInput/BaseInput.d.ts +4 -0
  126. package/dist/components/old/BaseInput/BaseInput.js +100 -0
  127. package/dist/components/old/BaseInput/BaseInput.types.d.ts +139 -0
  128. package/dist/components/old/BaseInput/BaseMultiSelect.d.ts +33 -0
  129. package/dist/components/old/BaseInput/BaseMultiSelect.js +68 -0
  130. package/dist/components/old/BaseInput/BaseRadio.d.ts +18 -0
  131. package/dist/components/old/BaseInput/BaseRadio.js +7 -0
  132. package/dist/components/old/BaseInput/BaseSelect.d.ts +41 -0
  133. package/dist/components/old/BaseInput/BaseSelect.js +83 -0
  134. package/dist/components/old/BaseInput/BaseTextInput.d.ts +27 -0
  135. package/dist/components/old/BaseInput/BaseTextInput.js +44 -0
  136. package/dist/components/old/BaseInput/BaseTextareaInput.d.ts +27 -0
  137. package/dist/components/old/BaseInput/BaseTextareaInput.js +36 -0
  138. package/dist/components/old/BaseInput/BaseToggle.d.ts +24 -0
  139. package/dist/components/old/BaseInput/BaseToggle.js +8 -0
  140. package/dist/components/old/BaseInput/DisabledSelect.d.ts +7 -0
  141. package/dist/components/old/BaseInput/DisabledSelect.js +6 -0
  142. package/dist/components/old/BaseInput/components/BaseErrorMessage.d.ts +8 -0
  143. package/dist/components/old/BaseInput/components/BaseErrorMessage.js +5 -0
  144. package/dist/components/old/BaseInput/components/CharacterLimitMessage.d.ts +9 -0
  145. package/dist/components/old/BaseInput/components/CharacterLimitMessage.js +7 -0
  146. package/dist/components/old/BaseInput/components/DropDownIndicator.d.ts +6 -0
  147. package/dist/components/old/BaseInput/components/DropDownIndicator.js +5 -0
  148. package/dist/components/old/BaseInput/components/MultiValueRemove.d.ts +4 -0
  149. package/dist/components/old/BaseInput/components/MultiValueRemove.js +7 -0
  150. package/dist/components/old/BaseInput/components/Option.d.ts +2 -0
  151. package/dist/components/old/BaseInput/components/Option.js +19 -0
  152. package/dist/components/old/BaseInput/components/SingleValue.d.ts +4 -0
  153. package/dist/components/old/BaseInput/components/SingleValue.js +14 -0
  154. package/dist/components/old/BaseInput/index.d.ts +1 -0
  155. package/dist/components/old/BaseInput/index.js +1 -0
  156. package/dist/components/old/Toaster/Toaster.d.ts +4 -0
  157. package/dist/components/old/Toaster/Toaster.js +20 -0
  158. package/dist/components/old/Toaster/index.d.ts +2 -0
  159. package/dist/components/old/Toaster/index.js +2 -0
  160. package/dist/components/old/Toaster/types.d.ts +17 -0
  161. package/dist/components/old/ToggleButton/ToggleButton.d.ts +4 -0
  162. package/dist/components/old/ToggleButton/ToggleButton.js +52 -0
  163. package/dist/components/old/ToggleButton/ToggleButton.types.d.ts +24 -0
  164. package/dist/components/old/ToggleButton/index.d.ts +1 -0
  165. package/dist/components/old/ToggleButton/index.js +1 -0
  166. package/dist/main.css +1 -1
  167. package/dist/reactQuery/queryHelpers.js +8 -4
  168. package/dist/templates/AddNotes/AddNotes.module.css +24 -1
  169. package/dist/templates/AddNotes/AddNotesTemplate.js +6 -7
  170. package/dist/templates/AddNotes/types.d.ts +31 -0
  171. package/dist/templates/PrimaryTable/PrimaryTable.js +270 -9
  172. package/dist/templates/PrimaryTable/PrimaryTableClientTemplate.js +2 -2
  173. package/dist/templates/PrimaryTable/PrimaryTableHeaderCell.d.ts +1 -1
  174. package/dist/templates/PrimaryTable/PrimaryTableHeaderCell.js +4 -4
  175. package/dist/templates/PrimaryTable/PrimaryTableServerTemplate.js +2 -2
  176. package/dist/templates/PrimaryTable/types.d.ts +5 -0
  177. package/dist/utils/getEndpointFromEnvironment.d.ts +20 -0
  178. package/dist/utils/getEndpointFromEnvironment.js +53 -0
  179. package/package.json +2 -2
  180. package/dist/components/Dropdown/Dropdown.d.ts +0 -4
  181. package/dist/components/Dropdown/Dropdown.js +0 -26
  182. package/dist/components/Dropdown/index.d.ts +0 -2
  183. package/dist/components/Dropdown/index.js +0 -2
  184. package/dist/components/Dropdown/types.d.ts +0 -19
  185. package/dist/components/ModalTag/ModalTag.d.ts +0 -4
  186. package/dist/components/ModalTag/ModalTag.js +0 -9
  187. package/dist/components/ModalTag/ModalTag.module.css +0 -22
  188. package/dist/components/ModalTag/index.d.ts +0 -3
  189. package/dist/components/ModalTag/index.js +0 -2
  190. package/dist/components/ModalTag/types.d.ts +0 -7
  191. package/dist/components/NotesList/NoteItem.d.ts +0 -4
  192. package/dist/components/NotesList/NoteItem.js +0 -7
  193. package/dist/components/NotesList/NotesList.d.ts +0 -4
  194. package/dist/components/NotesList/NotesList.js +0 -7
  195. package/dist/components/NotesList/index.d.ts +0 -2
  196. package/dist/components/NotesList/index.js +0 -2
  197. package/dist/components/NotesList/types.d.ts +0 -50
  198. package/dist/components/SearchInput/BaseSearchInput.d.ts +0 -4
  199. package/dist/components/SearchInput/BaseSearchInput.js +0 -21
  200. package/dist/components/SearchInput/SearchDropdown.scss +0 -41
  201. package/dist/components/SearchInput/SearchDropdownInput.d.ts +0 -36
  202. package/dist/components/SearchInput/SearchDropdownInput.js +0 -122
  203. package/dist/components/SearchInput/SearchInput.d.ts +0 -3
  204. package/dist/components/SearchInput/SearchInput.js +0 -32
  205. package/dist/components/SearchInput/SearchInput.types.d.ts +0 -109
  206. package/dist/components/SearchInput/SearchInputDatePicker.d.ts +0 -47
  207. package/dist/components/SearchInput/SearchInputDatePicker.js +0 -388
  208. package/dist/components/SearchInput/SearchNumberInput.d.ts +0 -66
  209. package/dist/components/SearchInput/SearchNumberInput.js +0 -169
  210. package/dist/components/SearchInput/SearchTextInput.d.ts +0 -29
  211. package/dist/components/SearchInput/SearchTextInput.js +0 -122
  212. package/dist/components/Table/components/RowMenu.d.ts +0 -4
  213. package/dist/components/Table/components/RowMenu.js +0 -23
  214. /package/dist/components/{Dropdown/types.js → AdvancedSelect/AdvancedSelect.types.js} +0 -0
  215. /package/dist/components/{ModalTag/types.js → old/BaseInput/BaseInput.types.js} +0 -0
  216. /package/dist/components/{NotesList → old/Toaster}/types.js +0 -0
  217. /package/dist/components/{SearchInput/SearchInput.types.js → old/ToggleButton/ToggleButton.types.js} +0 -0
@@ -3,14 +3,6 @@
3
3
  // Supply-specific logic lives in the consuming app via additionalData/extraWhereConditions
4
4
  import { apiGet } from "../apiFunctions.js";
5
5
  export const getDataTableData = async (tableViewMeta, additionalData = {}, extraFields = [], searchParams, options) => {
6
- // console.group(`[getDataTableData] slug: ${dataTableMeta?.slug}`);
7
- // console.log("dataTableMeta:", dataTableMeta);
8
- // console.log("additionalData:", additionalData);
9
- // console.log("extraFields:", extraFields);
10
- // console.log(
11
- // "searchParams:",
12
- // Object.fromEntries((searchParams ?? new URLSearchParams()).entries()),
13
- // );
14
6
  if (!tableViewMeta) {
15
7
  console.error("[getDataTableData] Missing table metadata");
16
8
  console.groupEnd();
@@ -22,7 +14,6 @@ export const getDataTableData = async (tableViewMeta, additionalData = {}, extra
22
14
  console.groupEnd();
23
15
  throw new Error(`tableViewMeta.fields is not iterable for slug: ${tableViewMeta.slug}`);
24
16
  }
25
- // console.log("tableViewFields:", tableViewFields);
26
17
  const parentTableSlug = tableViewMeta.slug;
27
18
  const queryParams = Object.fromEntries(searchParams);
28
19
  const isInfinite = Boolean(additionalData.hasInfiniteScroll);
@@ -84,8 +75,17 @@ export const getDataTableData = async (tableViewMeta, additionalData = {}, extra
84
75
  const stripBackticks = (s) => s.replace(/`/g, "");
85
76
  for (const j of tableViewMeta.joins) {
86
77
  const key = j.type === "OUTER" ? "ojoin" : "join";
78
+ // When a join aliases its table (e.g. joining `Addresses` a second
79
+ // time as `Addresses_C`), emit the `table@alias` form so the API
80
+ // declares the alias. Without this, two joins on the same physical
81
+ // table collide → MySQL 1066 "Not unique table/alias".
82
+ const tableKey = j.alias && j.alias !== j.table
83
+ ? `${j.table}@${j.alias}`
84
+ : j.table;
87
85
  apiOptions[key].push({
88
- [j.table]: { [stripBackticks(j.onA)]: [stripBackticks(j.onB)] },
86
+ [tableKey]: {
87
+ [stripBackticks(j.onA)]: [stripBackticks(j.onB)],
88
+ },
89
89
  });
90
90
  }
91
91
  if (apiOptions.join.length === 0)
@@ -457,11 +457,6 @@ export const getDataTableData = async (tableViewMeta, additionalData = {}, extra
457
457
  apiOptions.where.and.push(...additionalData.extraWhereConditions);
458
458
  }
459
459
  // ── Fetch ─────────────────────────────────────────────────────
460
- // console.log(
461
- // "[getDataTableData] Final apiOptions:",
462
- // JSON.stringify(apiOptions, null, 2),
463
- // );
464
- // console.log("[getDataTableData] Fetching route:", dataTableMeta.route);
465
460
  let parentResponse;
466
461
  try {
467
462
  parentResponse = (await apiGet(tableViewMeta.route, apiOptions));
@@ -471,15 +466,12 @@ export const getDataTableData = async (tableViewMeta, additionalData = {}, extra
471
466
  console.groupEnd();
472
467
  throw err;
473
468
  }
474
- // console.log("[getDataTableData] Raw response:", parentResponse);
475
469
  if (!parentResponse.isSuccess) {
476
470
  console.error("[getDataTableData] Response not successful:", parentResponse);
477
471
  options?.onError?.("parentResponse", parentResponse);
478
472
  console.groupEnd();
479
473
  return;
480
474
  }
481
- console.log("[getDataTableData] Success. Records count:", parentResponse?.data?.length ?? "unknown");
482
- console.groupEnd();
483
475
  return parentResponse;
484
476
  };
485
477
  // ── Page meta ─────────────────────────────────────────────────────
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,3 @@
1
+ import type { AdvancedSelectProps } from "./AdvancedSelect.types.js";
2
+ declare function AdvancedSelect<T>(props: AdvancedSelectProps<T>): import("react/jsx-runtime").JSX.Element;
3
+ export default AdvancedSelect;
@@ -0,0 +1,294 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
3
+ import { useVirtualizer } from "@tanstack/react-virtual";
4
+ import classes from "./AdvancedSelect.module.css";
5
+ import { getFontAwesomeIcon } from "../../utils/index.js";
6
+ import BaseErrorMessage from "../BaseInput/components/BaseErrorMessage.js";
7
+ const cx = (...parts) => parts.filter(Boolean).join(" ");
8
+ const getAvatarInitials = (name) => name
9
+ .trim()
10
+ .split(/\s+/)
11
+ .slice(0, 2)
12
+ .map((part) => part.charAt(0).toUpperCase())
13
+ .join("");
14
+ function OptionAvatar({ name, className, }) {
15
+ const initials = getAvatarInitials(name); // Styling (including background color) is fully CSS-driven via the // `--advancedSelect-avatar-*` custom properties and the `avatarClassName` // prop. See AdvancedSelect.module.css for the available variables.
16
+ return (_jsx("span", { className: cx(classes.avatar, className), "aria-hidden": "true", children: initials }));
17
+ }
18
+ function AdvancedSelect(props) {
19
+ const { errorMessage, isFieldDirty, isFieldError, mode = "single", label, placeholder = "Search...", disabled = false, data, getOptionValue, getOptionLabel, getOptionSecondaryLabel, renderOption, showAvatar = false, getOptionAvatarText, avatarClassName, hasMorePages = false, isFetchingMore = false, fetchMoreData, findSpecificValue, searchDebounceMs = 350, searchMinChars = 3, noOptionsMessage = "No options", loadingMessage = "Loading...", rowHeight = 36, maxVisibleRows = 8, overscan = 6, className, menuClassName, onClearInput, onOpen, onClose, valueKey, } = props;
20
+ const isMulti = mode === "multi";
21
+ const [open, setOpen] = useState(false);
22
+ const [input, setInput] = useState("");
23
+ const [searchResults, setSearchResults] = useState(null);
24
+ const [searching, setSearching] = useState(false);
25
+ const [activeIndex, setActiveIndex] = useState(0);
26
+ const wrapperRef = useRef(null);
27
+ const inputRef = useRef(null);
28
+ const listRef = useRef(null);
29
+ const debounceRef = useRef(null);
30
+ const abortRef = useRef(null); // Build options from current data source. // - With async search (findSpecificValue): use server results once length >= min chars. // - Without async search: filter `data` client-side by input text.
31
+ const sourceData = searchResults ?? data;
32
+ const allOptions = useMemo(() => (sourceData ?? []).map((item) => ({
33
+ value: getOptionValue(item),
34
+ label: getOptionLabel(item),
35
+ secondaryLabel: getOptionSecondaryLabel?.(item),
36
+ item,
37
+ })), [sourceData, getOptionValue, getOptionLabel, getOptionSecondaryLabel]); // Selected values as Set for fast lookup
38
+ const selectedValueSet = useMemo(() => {
39
+ const set = new Set();
40
+ if (isMulti) {
41
+ (props.value ?? []).forEach((it) => set.add(getOptionValue(it)));
42
+ }
43
+ else if (props.value) {
44
+ set.add(getOptionValue(props.value));
45
+ }
46
+ return set;
47
+ }, [isMulti, props.value, getOptionValue]);
48
+ const options = useMemo(() => {
49
+ const q = input.trim().toLowerCase();
50
+ const filtered = findSpecificValue || !q
51
+ ? allOptions
52
+ : allOptions.filter((o) => o.label.toLowerCase().includes(q)); // Float selected option(s) to the top while preserving the relative // order of everything else (stable sort).
53
+ if (selectedValueSet.size === 0)
54
+ return filtered;
55
+ const selected = filtered.filter((o) => selectedValueSet.has(o.value));
56
+ const rest = filtered.filter((o) => !selectedValueSet.has(o.value));
57
+ return [...selected, ...rest];
58
+ }, [allOptions, input, findSpecificValue, selectedValueSet]); // Debounced server-side search
59
+ useEffect(() => {
60
+ if (!findSpecificValue)
61
+ return;
62
+ if (input.length < searchMinChars) {
63
+ setSearchResults(null);
64
+ setSearching(false);
65
+ return;
66
+ }
67
+ setSearching(true);
68
+ if (debounceRef.current)
69
+ clearTimeout(debounceRef.current);
70
+ if (abortRef.current)
71
+ abortRef.current.abort();
72
+ const controller = new AbortController();
73
+ abortRef.current = controller;
74
+ debounceRef.current = setTimeout(async () => {
75
+ try {
76
+ const results = await findSpecificValue(input);
77
+ if (!controller.signal.aborted)
78
+ setSearchResults(results ?? []);
79
+ }
80
+ catch (err) {
81
+ if (!controller.signal.aborted)
82
+ setSearchResults([]);
83
+ }
84
+ finally {
85
+ if (!controller.signal.aborted)
86
+ setSearching(false);
87
+ }
88
+ }, searchDebounceMs);
89
+ return () => {
90
+ if (debounceRef.current)
91
+ clearTimeout(debounceRef.current);
92
+ controller.abort();
93
+ };
94
+ }, [input, findSpecificValue, searchDebounceMs, searchMinChars]); // Outside click → close
95
+ useEffect(() => {
96
+ if (!open)
97
+ return;
98
+ const handler = (e) => {
99
+ if (!wrapperRef.current?.contains(e.target)) {
100
+ closeMenu();
101
+ }
102
+ };
103
+ document.addEventListener("mousedown", handler);
104
+ return () => document.removeEventListener("mousedown", handler); // eslint-disable-next-line react-hooks/exhaustive-deps
105
+ }, [open]);
106
+ const openMenu = useCallback(() => {
107
+ if (disabled || open)
108
+ return;
109
+ setOpen(true);
110
+ onOpen?.();
111
+ }, [disabled, open, onOpen]);
112
+ const closeMenu = useCallback(() => {
113
+ setOpen(false);
114
+ setActiveIndex(0);
115
+ onClose?.();
116
+ }, [onClose]); // Virtualizer
117
+ const showLoaderRow = hasMorePages && !searchResults;
118
+ const totalRows = options.length + (showLoaderRow ? 1 : 0);
119
+ const rowVirtualizer = useVirtualizer({
120
+ count: totalRows,
121
+ getScrollElement: () => listRef.current,
122
+ estimateSize: () => rowHeight,
123
+ overscan,
124
+ }); // Infinite scroll trigger
125
+ useEffect(() => {
126
+ if (!open || !fetchMoreData || !hasMorePages || isFetchingMore)
127
+ return;
128
+ if (searchResults)
129
+ return; // don't auto-fetch while searching
130
+ const items = rowVirtualizer.getVirtualItems();
131
+ const last = items[items.length - 1];
132
+ if (last && last.index >= options.length - 1) {
133
+ void fetchMoreData();
134
+ }
135
+ }, [
136
+ open,
137
+ rowVirtualizer.getVirtualItems(),
138
+ fetchMoreData,
139
+ hasMorePages,
140
+ isFetchingMore,
141
+ searchResults,
142
+ options.length,
143
+ ]);
144
+ const clearAll = useCallback(() => {
145
+ setInput("");
146
+ setSearchResults(null);
147
+ if (isMulti) {
148
+ props.onSelect([], valueKey);
149
+ }
150
+ else {
151
+ props.onSelect(null, valueKey);
152
+ }
153
+ onClearInput?.();
154
+ }, [isMulti, props.onSelect, valueKey, onClearInput]);
155
+ const handleSelect = useCallback((option) => {
156
+ if (isMulti) {
157
+ const current = props.value ?? [];
158
+ const already = selectedValueSet.has(option.value);
159
+ const next = already
160
+ ? current.filter((it) => getOptionValue(it) !== option.value)
161
+ : [...current, option.item];
162
+ props.onSelect(next, valueKey);
163
+ setInput("");
164
+ inputRef.current?.focus();
165
+ }
166
+ else {
167
+ props.onSelect(option.item, valueKey); // Clear the search text (not set it to the label) so reopening // the menu shows the full options list instead of filtering it // down to the selected label. The selected label is still shown // in the closed input via `singleSelectedLabel`.
168
+ setInput("");
169
+ closeMenu();
170
+ }
171
+ }, [
172
+ isMulti,
173
+ props.value,
174
+ props.onSelect,
175
+ selectedValueSet,
176
+ getOptionValue,
177
+ valueKey,
178
+ closeMenu,
179
+ ]);
180
+ const removeChip = useCallback((itemValue) => {
181
+ if (!isMulti)
182
+ return;
183
+ const current = props.value ?? [];
184
+ const next = current.filter((it) => getOptionValue(it) !== itemValue);
185
+ props.onSelect(next, valueKey);
186
+ }, [isMulti, props.value, props.onSelect, getOptionValue, valueKey]);
187
+ const onKeyDown = (e) => {
188
+ if (e.key === "ArrowDown") {
189
+ e.preventDefault();
190
+ openMenu();
191
+ setActiveIndex((i) => Math.min(i + 1, options.length - 1));
192
+ }
193
+ else if (e.key === "ArrowUp") {
194
+ e.preventDefault();
195
+ setActiveIndex((i) => Math.max(i - 1, 0));
196
+ }
197
+ else if (e.key === "Enter") {
198
+ if (open && options[activeIndex]) {
199
+ e.preventDefault();
200
+ handleSelect(options[activeIndex]);
201
+ }
202
+ }
203
+ else if (e.key === "Escape") {
204
+ closeMenu();
205
+ }
206
+ else if (e.key === "Backspace" &&
207
+ isMulti &&
208
+ input === "" &&
209
+ props.value?.length) {
210
+ const list = props.value;
211
+ removeChip(getOptionValue(list[list.length - 1]));
212
+ }
213
+ }; // Display value for single mode when not focused/typing
214
+ const singleSelectedLabel = !isMulti && props.value ? getOptionLabel(props.value) : "";
215
+ const singleSelectedSecondaryLabel = !isMulti && props.value
216
+ ? getOptionSecondaryLabel?.(props.value) ?? ""
217
+ : "";
218
+ // When a single value has a secondary label and the menu is closed, show a
219
+ // styled label+secondary node instead of the plain native input text.
220
+ const showStyledSingleValue = !isMulti && !!props.value && !open && !!singleSelectedSecondaryLabel;
221
+ const displayedInput = isMulti
222
+ ? input
223
+ : open
224
+ ? input
225
+ : singleSelectedLabel || input;
226
+ const chips = isMulti && props.value?.length
227
+ ? props.value.map((it) => ({
228
+ value: getOptionValue(it),
229
+ label: getOptionLabel(it),
230
+ avatarText: getOptionAvatarText?.(it) ?? getOptionLabel(it),
231
+ }))
232
+ : []; // Avatar shown alongside the selected option in single mode, while the // selected label (not an active search) is displayed in the input.
233
+ const showSingleAvatar = !isMulti &&
234
+ showAvatar &&
235
+ !!props.value &&
236
+ !open &&
237
+ !!singleSelectedLabel;
238
+ const singleAvatarText = showSingleAvatar
239
+ ? getOptionAvatarText?.(props.value) ?? singleSelectedLabel
240
+ : "";
241
+ const virtualItems = rowVirtualizer.getVirtualItems();
242
+ const hasAnyValue = isMulti
243
+ ? chips.length > 0 || input.length > 0
244
+ : !!props.value || input.length > 0;
245
+ return (_jsxs("div", { ref: wrapperRef, className: cx(classes.wrapper, className, mode === "multi" && classes.multi), children: [label && _jsx("label", { className: classes.label, children: label }), _jsxs("div", { className: cx(classes.control, disabled && classes.disabled, isFieldError
246
+ ? classes.error
247
+ : isFieldDirty && classes.dirty), onClick: () => {
248
+ if (!disabled) {
249
+ openMenu();
250
+ inputRef.current?.focus();
251
+ }
252
+ }, children: [_jsxs("div", { className: classes.chipsAndInput, children: [chips.map((c) => (_jsxs("span", { className: classes.chip, children: [showAvatar && (_jsx("div", { className: "pr-0.5", children: _jsx(OptionAvatar, { name: singleAvatarText, className: avatarClassName }) })), c.label, _jsx("button", { type: "button", className: classes.chipRemove, onClick: (e) => {
253
+ e.stopPropagation();
254
+ removeChip(c.value);
255
+ }, "aria-label": `Remove ${c.label}`, children: getFontAwesomeIcon("xmark", "regular") })] }, c.value))), showSingleAvatar && (_jsx("div", { className: "pr-0.5", children: _jsx(OptionAvatar, { name: singleAvatarText, className: avatarClassName }) })), showStyledSingleValue && (_jsxs("span", { className: classes.singleValue, children: [_jsx("span", { className: classes.optionPrimary, children: singleSelectedLabel }), _jsx("span", { className: classes.optionSecondary, children: singleSelectedSecondaryLabel })] })), _jsx("input", { ref: inputRef, id: valueKey, type: "text", className: cx(classes.input, mode === "multi" && classes.multiInput, showStyledSingleValue && classes.inputCollapsed), value: showStyledSingleValue ? "" : displayedInput, placeholder: chips.length === 0 ? placeholder : "", disabled: disabled, autoComplete: "off", onChange: (e) => {
256
+ setInput(e.target.value);
257
+ openMenu();
258
+ }, onFocus: openMenu, onKeyDown: onKeyDown })] }), _jsxs("div", { className: classes.indicators, children: [hasAnyValue && (_jsx("button", { type: "button", className: classes.indicatorBtn, disabled: disabled, onClick: (e) => {
259
+ e.stopPropagation();
260
+ clearAll();
261
+ }, "aria-label": "Clear", children: _jsx("span", { className: classes.clearSingleInput, children: getFontAwesomeIcon("xmark", "regular") }) })), _jsx("button", { type: "button", className: classes.indicatorBtn, disabled: disabled, onClick: (e) => {
262
+ e.stopPropagation();
263
+ if (open)
264
+ closeMenu();
265
+ else {
266
+ openMenu();
267
+ inputRef.current?.focus();
268
+ }
269
+ }, "aria-label": "Toggle menu", children: getFontAwesomeIcon("chevronDown", "regular") })] })] }), open && (_jsx("div", { className: cx(classes.menu, menuClassName), children: _jsx("div", { ref: listRef, className: classes.menuList, style: {
270
+ maxHeight: `${rowHeight * maxVisibleRows}px`,
271
+ }, children: searching ? (_jsx("div", { className: classes.statusRow, children: loadingMessage })) : options.length === 0 ? (_jsx("div", { className: classes.statusRow, children: noOptionsMessage })) : (_jsx("div", { className: classes.virtualRows, style: {
272
+ height: `${rowVirtualizer.getTotalSize()}px`,
273
+ }, children: virtualItems.map((vRow) => {
274
+ const isLoaderRow = showLoaderRow &&
275
+ vRow.index === options.length;
276
+ const style = {
277
+ position: "absolute",
278
+ top: 0,
279
+ left: 0,
280
+ width: "100%",
281
+ transform: `translateY(${vRow.start}px)`,
282
+ height: `${vRow.size}px`,
283
+ };
284
+ if (isLoaderRow) {
285
+ return (_jsx("div", { style: style, className: classes.shimmerRow }, "loader"));
286
+ }
287
+ const option = options[vRow.index];
288
+ const isSelected = selectedValueSet.has(option.value);
289
+ const isActive = vRow.index === activeIndex;
290
+ return (_jsx("div", { style: style, className: cx(classes.option, isSelected && classes.selected, isActive && classes.active), onMouseEnter: () => setActiveIndex(vRow.index), onMouseDown: (e) => e.preventDefault(), onClick: () => handleSelect(option), children: _jsxs("label", { className: classes.optionLabel, children: [mode === "multi" ? (_jsxs(_Fragment, { children: [_jsx("input", { type: "checkbox", checked: isSelected, onChange: () => handleSelect(option) }), _jsx("span", { className: classes.checkIcon, children: getFontAwesomeIcon("check", "regular") })] })) : (_jsx(_Fragment, { children: isSelected && (_jsx("span", { className: classes.optionCheck, children: getFontAwesomeIcon("check", "regular") })) })), showAvatar && (_jsx("div", { className: "pr-0.5", children: _jsx(OptionAvatar, { name: getOptionAvatarText?.(option.item) ??
291
+ option.label, className: avatarClassName }) })), option.secondaryLabel ? (_jsxs("span", { className: classes.optionText, children: [_jsx("span", { className: classes.optionPrimary, children: option.label }), _jsx("span", { className: classes.optionSecondary, children: option.secondaryLabel })] })) : (_jsx("span", { children: option.label }))] }) }, option.value));
292
+ }) })) }) })), errorMessage && _jsx(BaseErrorMessage, { errorMessage: errorMessage })] }));
293
+ }
294
+ export default AdvancedSelect;