@fgv/ts-app-shell 5.1.0-1

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 (180) hide show
  1. package/README.md +26 -0
  2. package/dist/index.browser.js +3 -0
  3. package/dist/index.js +43 -0
  4. package/dist/packlets/ai-assist/index.js +6 -0
  5. package/dist/packlets/ai-assist/useAiAssist.js +219 -0
  6. package/dist/packlets/cascade/CascadeContainer.js +83 -0
  7. package/dist/packlets/cascade/ComparisonView.js +48 -0
  8. package/dist/packlets/cascade/EntityTabLayout.js +104 -0
  9. package/dist/packlets/cascade/MobileCascadeStack.js +63 -0
  10. package/dist/packlets/cascade/index.js +37 -0
  11. package/dist/packlets/cascade/model.js +30 -0
  12. package/dist/packlets/cascade/useCascadeOps.js +206 -0
  13. package/dist/packlets/cascade/useCascadeTransitions.js +58 -0
  14. package/dist/packlets/detail/DetailHelpers.js +103 -0
  15. package/dist/packlets/detail/index.js +6 -0
  16. package/dist/packlets/drop-zone/JsonDropZone.js +112 -0
  17. package/dist/packlets/drop-zone/index.js +6 -0
  18. package/dist/packlets/editing/EditFieldHelpers.js +130 -0
  19. package/dist/packlets/editing/MultiActionButton.js +73 -0
  20. package/dist/packlets/editing/NumericInput.js +119 -0
  21. package/dist/packlets/editing/TypeaheadInput.js +207 -0
  22. package/dist/packlets/editing/index.js +10 -0
  23. package/dist/packlets/editing/useTypeaheadMatch.js +102 -0
  24. package/dist/packlets/keyboard/index.js +7 -0
  25. package/dist/packlets/keyboard/registry.js +133 -0
  26. package/dist/packlets/keyboard/useKeyboardShortcuts.js +117 -0
  27. package/dist/packlets/messages/MessagesContext.js +76 -0
  28. package/dist/packlets/messages/MessagesLogger.js +103 -0
  29. package/dist/packlets/messages/StatusBar.js +154 -0
  30. package/dist/packlets/messages/Toast.js +68 -0
  31. package/dist/packlets/messages/index.js +11 -0
  32. package/dist/packlets/messages/model.js +56 -0
  33. package/dist/packlets/messages/useLogReporter.js +66 -0
  34. package/dist/packlets/modal/ConfirmDialog.js +78 -0
  35. package/dist/packlets/modal/Modal.js +55 -0
  36. package/dist/packlets/modal/index.js +7 -0
  37. package/dist/packlets/print/PrintEnclosure.js +60 -0
  38. package/dist/packlets/print/index.js +7 -0
  39. package/dist/packlets/print/openPrintWindow.js +112 -0
  40. package/dist/packlets/responsive/ResponsiveProvider.js +56 -0
  41. package/dist/packlets/responsive/index.js +7 -0
  42. package/dist/packlets/responsive/useResponsiveLayout.js +118 -0
  43. package/dist/packlets/selectors/EntityRow.js +276 -0
  44. package/dist/packlets/selectors/PreferredSelector.js +251 -0
  45. package/dist/packlets/selectors/index.js +24 -0
  46. package/dist/packlets/sidebar/CollectionSection.js +107 -0
  47. package/dist/packlets/sidebar/EntityList.js +164 -0
  48. package/dist/packlets/sidebar/FilterBar.js +42 -0
  49. package/dist/packlets/sidebar/FilterRow.js +182 -0
  50. package/dist/packlets/sidebar/GroupedEntityList.js +183 -0
  51. package/dist/packlets/sidebar/SearchBar.js +34 -0
  52. package/dist/packlets/sidebar/SidebarLayout.js +62 -0
  53. package/dist/packlets/sidebar/index.js +12 -0
  54. package/dist/packlets/theme/ThemeProvider.js +141 -0
  55. package/dist/packlets/theme/index.js +6 -0
  56. package/dist/packlets/top-bar/ModeSelector.js +46 -0
  57. package/dist/packlets/top-bar/TabBar.js +37 -0
  58. package/dist/packlets/top-bar/index.js +7 -0
  59. package/dist/packlets/url-sync/index.js +6 -0
  60. package/dist/packlets/url-sync/useUrlSync.js +157 -0
  61. package/eslint.config.js +22 -0
  62. package/lib/index.browser.d.ts +2 -0
  63. package/lib/index.browser.js +19 -0
  64. package/lib/index.d.ts +28 -0
  65. package/lib/index.js +59 -0
  66. package/lib/packlets/ai-assist/index.d.ts +6 -0
  67. package/lib/packlets/ai-assist/index.js +11 -0
  68. package/lib/packlets/ai-assist/useAiAssist.d.ts +77 -0
  69. package/lib/packlets/ai-assist/useAiAssist.js +223 -0
  70. package/lib/packlets/cascade/CascadeContainer.d.ts +44 -0
  71. package/lib/packlets/cascade/CascadeContainer.js +119 -0
  72. package/lib/packlets/cascade/ComparisonView.d.ts +35 -0
  73. package/lib/packlets/cascade/ComparisonView.js +54 -0
  74. package/lib/packlets/cascade/EntityTabLayout.d.ts +47 -0
  75. package/lib/packlets/cascade/EntityTabLayout.js +110 -0
  76. package/lib/packlets/cascade/MobileCascadeStack.d.ts +20 -0
  77. package/lib/packlets/cascade/MobileCascadeStack.js +99 -0
  78. package/lib/packlets/cascade/index.d.ts +12 -0
  79. package/lib/packlets/cascade/index.js +48 -0
  80. package/lib/packlets/cascade/model.d.ts +57 -0
  81. package/lib/packlets/cascade/model.js +33 -0
  82. package/lib/packlets/cascade/useCascadeOps.d.ts +111 -0
  83. package/lib/packlets/cascade/useCascadeOps.js +209 -0
  84. package/lib/packlets/cascade/useCascadeTransitions.d.ts +19 -0
  85. package/lib/packlets/cascade/useCascadeTransitions.js +62 -0
  86. package/lib/packlets/detail/DetailHelpers.d.ts +83 -0
  87. package/lib/packlets/detail/DetailHelpers.js +113 -0
  88. package/lib/packlets/detail/index.d.ts +6 -0
  89. package/lib/packlets/detail/index.js +14 -0
  90. package/lib/packlets/drop-zone/JsonDropZone.d.ts +40 -0
  91. package/lib/packlets/drop-zone/JsonDropZone.js +149 -0
  92. package/lib/packlets/drop-zone/index.d.ts +6 -0
  93. package/lib/packlets/drop-zone/index.js +10 -0
  94. package/lib/packlets/editing/EditFieldHelpers.d.ts +171 -0
  95. package/lib/packlets/editing/EditFieldHelpers.js +144 -0
  96. package/lib/packlets/editing/MultiActionButton.d.ts +45 -0
  97. package/lib/packlets/editing/MultiActionButton.js +109 -0
  98. package/lib/packlets/editing/NumericInput.d.ts +47 -0
  99. package/lib/packlets/editing/NumericInput.js +155 -0
  100. package/lib/packlets/editing/TypeaheadInput.d.ts +46 -0
  101. package/lib/packlets/editing/TypeaheadInput.js +243 -0
  102. package/lib/packlets/editing/index.d.ts +10 -0
  103. package/lib/packlets/editing/index.js +26 -0
  104. package/lib/packlets/editing/useTypeaheadMatch.d.ts +42 -0
  105. package/lib/packlets/editing/useTypeaheadMatch.js +105 -0
  106. package/lib/packlets/keyboard/index.d.ts +7 -0
  107. package/lib/packlets/keyboard/index.js +15 -0
  108. package/lib/packlets/keyboard/registry.d.ts +92 -0
  109. package/lib/packlets/keyboard/registry.js +138 -0
  110. package/lib/packlets/keyboard/useKeyboardShortcuts.d.ts +50 -0
  111. package/lib/packlets/keyboard/useKeyboardShortcuts.js +155 -0
  112. package/lib/packlets/messages/MessagesContext.d.ts +40 -0
  113. package/lib/packlets/messages/MessagesContext.js +113 -0
  114. package/lib/packlets/messages/MessagesLogger.d.ts +50 -0
  115. package/lib/packlets/messages/MessagesLogger.js +107 -0
  116. package/lib/packlets/messages/StatusBar.d.ts +22 -0
  117. package/lib/packlets/messages/StatusBar.js +190 -0
  118. package/lib/packlets/messages/Toast.d.ts +31 -0
  119. package/lib/packlets/messages/Toast.js +105 -0
  120. package/lib/packlets/messages/index.d.ts +11 -0
  121. package/lib/packlets/messages/index.js +24 -0
  122. package/lib/packlets/messages/model.d.ts +59 -0
  123. package/lib/packlets/messages/model.js +61 -0
  124. package/lib/packlets/messages/useLogReporter.d.ts +22 -0
  125. package/lib/packlets/messages/useLogReporter.js +69 -0
  126. package/lib/packlets/modal/ConfirmDialog.d.ts +39 -0
  127. package/lib/packlets/modal/ConfirmDialog.js +114 -0
  128. package/lib/packlets/modal/Modal.d.ts +22 -0
  129. package/lib/packlets/modal/Modal.js +91 -0
  130. package/lib/packlets/modal/index.d.ts +7 -0
  131. package/lib/packlets/modal/index.js +12 -0
  132. package/lib/packlets/print/PrintEnclosure.d.ts +33 -0
  133. package/lib/packlets/print/PrintEnclosure.js +96 -0
  134. package/lib/packlets/print/index.d.ts +7 -0
  135. package/lib/packlets/print/index.js +12 -0
  136. package/lib/packlets/print/openPrintWindow.d.ts +35 -0
  137. package/lib/packlets/print/openPrintWindow.js +118 -0
  138. package/lib/packlets/responsive/ResponsiveProvider.d.ts +35 -0
  139. package/lib/packlets/responsive/ResponsiveProvider.js +93 -0
  140. package/lib/packlets/responsive/index.d.ts +7 -0
  141. package/lib/packlets/responsive/index.js +13 -0
  142. package/lib/packlets/responsive/useResponsiveLayout.d.ts +48 -0
  143. package/lib/packlets/responsive/useResponsiveLayout.js +121 -0
  144. package/lib/packlets/selectors/EntityRow.d.ts +45 -0
  145. package/lib/packlets/selectors/EntityRow.js +315 -0
  146. package/lib/packlets/selectors/PreferredSelector.d.ts +50 -0
  147. package/lib/packlets/selectors/PreferredSelector.js +287 -0
  148. package/lib/packlets/selectors/index.d.ts +5 -0
  149. package/lib/packlets/selectors/index.js +29 -0
  150. package/lib/packlets/sidebar/CollectionSection.d.ts +82 -0
  151. package/lib/packlets/sidebar/CollectionSection.js +143 -0
  152. package/lib/packlets/sidebar/EntityList.d.ts +105 -0
  153. package/lib/packlets/sidebar/EntityList.js +200 -0
  154. package/lib/packlets/sidebar/FilterBar.d.ts +26 -0
  155. package/lib/packlets/sidebar/FilterBar.js +48 -0
  156. package/lib/packlets/sidebar/FilterRow.d.ts +42 -0
  157. package/lib/packlets/sidebar/FilterRow.js +218 -0
  158. package/lib/packlets/sidebar/GroupedEntityList.d.ts +59 -0
  159. package/lib/packlets/sidebar/GroupedEntityList.js +219 -0
  160. package/lib/packlets/sidebar/SearchBar.d.ts +19 -0
  161. package/lib/packlets/sidebar/SearchBar.js +40 -0
  162. package/lib/packlets/sidebar/SidebarLayout.d.ts +28 -0
  163. package/lib/packlets/sidebar/SidebarLayout.js +98 -0
  164. package/lib/packlets/sidebar/index.d.ts +12 -0
  165. package/lib/packlets/sidebar/index.js +22 -0
  166. package/lib/packlets/theme/ThemeProvider.d.ts +68 -0
  167. package/lib/packlets/theme/ThemeProvider.js +178 -0
  168. package/lib/packlets/theme/index.d.ts +6 -0
  169. package/lib/packlets/theme/index.js +11 -0
  170. package/lib/packlets/top-bar/ModeSelector.d.ts +38 -0
  171. package/lib/packlets/top-bar/ModeSelector.js +52 -0
  172. package/lib/packlets/top-bar/TabBar.d.ts +31 -0
  173. package/lib/packlets/top-bar/TabBar.js +43 -0
  174. package/lib/packlets/top-bar/index.d.ts +7 -0
  175. package/lib/packlets/top-bar/index.js +12 -0
  176. package/lib/packlets/url-sync/index.d.ts +6 -0
  177. package/lib/packlets/url-sync/index.js +12 -0
  178. package/lib/packlets/url-sync/useUrlSync.d.ts +75 -0
  179. package/lib/packlets/url-sync/useUrlSync.js +162 -0
  180. package/package.json +82 -0
@@ -0,0 +1,315 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2026 Erik Fortune
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ * SOFTWARE.
22
+ */
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
35
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
36
+ }) : function(o, v) {
37
+ o["default"] = v;
38
+ });
39
+ var __importStar = (this && this.__importStar) || (function () {
40
+ var ownKeys = function(o) {
41
+ ownKeys = Object.getOwnPropertyNames || function (o) {
42
+ var ar = [];
43
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
44
+ return ar;
45
+ };
46
+ return ownKeys(o);
47
+ };
48
+ return function (mod) {
49
+ if (mod && mod.__esModule) return mod;
50
+ var result = {};
51
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
52
+ __setModuleDefault(result, mod);
53
+ return result;
54
+ };
55
+ })();
56
+ var __importDefault = (this && this.__importDefault) || function (mod) {
57
+ return (mod && mod.__esModule) ? mod : { "default": mod };
58
+ };
59
+ Object.defineProperty(exports, "__esModule", { value: true });
60
+ exports.EntityRow = EntityRow;
61
+ /**
62
+ * EntityRow — clickable text row with optional discrete swap-icon for alternates.
63
+ *
64
+ * Renders a uniform row regardless of whether alternates exist:
65
+ * - Left slot: swap icon (when alternates exist) or empty spacer
66
+ * - Name text: clickable for drill-down
67
+ * - Optional right-side content (amount, sublabel, etc.)
68
+ * - Drill-down chevron ›
69
+ *
70
+ * @packageDocumentation
71
+ */
72
+ const react_1 = __importStar(require("react"));
73
+ const react_dom_1 = __importDefault(require("react-dom"));
74
+ // ============================================================================
75
+ // EntityRow Component
76
+ // ============================================================================
77
+ /**
78
+ * A clickable text row with an optional discrete swap-icon popover for
79
+ * switching between alternates. Clicking the row itself always triggers
80
+ * drill-down navigation. The swap icon opens a small picker to change
81
+ * which alternate is displayed.
82
+ *
83
+ * @public
84
+ */
85
+ function EntityRow(props) {
86
+ var _a, _b, _c, _d;
87
+ const { items, preferredId, onClick, onSelect, onCompare, rightContent, label } = props;
88
+ const hasAlternates = items.length > 1;
89
+ const [internalId, setInternalId] = (0, react_1.useState)((_b = preferredId !== null && preferredId !== void 0 ? preferredId : (_a = items[0]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '');
90
+ const displayedId = (_c = props.selectedId) !== null && _c !== void 0 ? _c : internalId;
91
+ const [pickerOpen, setPickerOpen] = (0, react_1.useState)(false);
92
+ const [compareMode, setCompareMode] = (0, react_1.useState)(false);
93
+ const [checkedIds, setCheckedIds] = (0, react_1.useState)(new Set());
94
+ const [focusIndex, setFocusIndex] = (0, react_1.useState)(-1);
95
+ const pickerRef = (0, react_1.useRef)(null);
96
+ const pickerBtnRef = (0, react_1.useRef)(null);
97
+ const itemRefs = (0, react_1.useRef)([]);
98
+ const [pickerPos, setPickerPos] = (0, react_1.useState)({ top: 0, left: 0 });
99
+ // Reset displayed item when items or preferred change (e.g. variation switch)
100
+ (0, react_1.useEffect)(() => {
101
+ var _a, _b;
102
+ setInternalId((_b = preferredId !== null && preferredId !== void 0 ? preferredId : (_a = items[0]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '');
103
+ }, [preferredId, items]);
104
+ // Reset compare mode when picker closes; set initial focus when it opens
105
+ (0, react_1.useEffect)(() => {
106
+ if (!pickerOpen) {
107
+ setCompareMode(false);
108
+ setCheckedIds(new Set());
109
+ setFocusIndex(-1);
110
+ }
111
+ else {
112
+ const idx = items.findIndex((i) => i.id === displayedId);
113
+ setFocusIndex(idx >= 0 ? idx : 0);
114
+ }
115
+ }, [pickerOpen, items, displayedId]);
116
+ const displayedItem = (0, react_1.useMemo)(() => { var _a; return (_a = items.find((i) => i.id === displayedId)) !== null && _a !== void 0 ? _a : items[0]; }, [items, displayedId]);
117
+ const isPreferred = displayedId === preferredId;
118
+ // Position the portal popover relative to the swap button
119
+ (0, react_1.useEffect)(() => {
120
+ if (pickerOpen && pickerBtnRef.current) {
121
+ const rect = pickerBtnRef.current.getBoundingClientRect();
122
+ setPickerPos({ top: rect.bottom + 2, left: rect.left });
123
+ }
124
+ }, [pickerOpen]);
125
+ // Close picker on outside click or scroll
126
+ (0, react_1.useEffect)(() => {
127
+ if (!pickerOpen) {
128
+ return;
129
+ }
130
+ const handleClickOutside = (e) => {
131
+ if (pickerRef.current &&
132
+ !pickerRef.current.contains(e.target) &&
133
+ pickerBtnRef.current &&
134
+ !pickerBtnRef.current.contains(e.target)) {
135
+ setPickerOpen(false);
136
+ }
137
+ };
138
+ const handleScroll = () => {
139
+ setPickerOpen(false);
140
+ };
141
+ document.addEventListener('mousedown', handleClickOutside);
142
+ // Close on any scroll so the popover doesn't float away from the button
143
+ document.addEventListener('scroll', handleScroll, true);
144
+ return () => {
145
+ document.removeEventListener('mousedown', handleClickOutside);
146
+ document.removeEventListener('scroll', handleScroll, true);
147
+ };
148
+ }, [pickerOpen]);
149
+ const handleRowClick = (0, react_1.useCallback)(() => {
150
+ if (onClick) {
151
+ onClick(displayedId);
152
+ }
153
+ }, [onClick, displayedId]);
154
+ const handleRowKeyDown = (0, react_1.useCallback)((e) => {
155
+ if (onClick && (e.key === 'Enter' || e.key === ' ')) {
156
+ e.preventDefault();
157
+ onClick(displayedId);
158
+ }
159
+ }, [onClick, displayedId]);
160
+ const handlePickerToggle = (0, react_1.useCallback)((e) => {
161
+ e.stopPropagation();
162
+ setPickerOpen((prev) => !prev);
163
+ }, []);
164
+ const handlePickItem = (0, react_1.useCallback)((id) => {
165
+ setInternalId(id);
166
+ if (onSelect) {
167
+ onSelect(id);
168
+ }
169
+ setPickerOpen(false);
170
+ }, [onSelect]);
171
+ const handleCompareAll = (0, react_1.useCallback)(() => {
172
+ if (onCompare) {
173
+ onCompare(items.map((i) => i.id));
174
+ }
175
+ setPickerOpen(false);
176
+ }, [onCompare, items]);
177
+ const toggleChecked = (0, react_1.useCallback)((id) => {
178
+ setCheckedIds((prev) => {
179
+ const next = new Set(prev);
180
+ if (next.has(id)) {
181
+ next.delete(id);
182
+ }
183
+ else if (next.size < 4) {
184
+ next.add(id);
185
+ }
186
+ return next;
187
+ });
188
+ }, []);
189
+ const handleCompareSelected = (0, react_1.useCallback)(() => {
190
+ if (onCompare && checkedIds.size >= 2) {
191
+ onCompare(Array.from(checkedIds));
192
+ setPickerOpen(false);
193
+ }
194
+ }, [onCompare, checkedIds]);
195
+ const handlePickerKeyDown = (0, react_1.useCallback)((e) => {
196
+ switch (e.key) {
197
+ case 'Escape': {
198
+ e.preventDefault();
199
+ e.stopPropagation();
200
+ if (compareMode) {
201
+ setCompareMode(false);
202
+ setCheckedIds(new Set());
203
+ }
204
+ else {
205
+ setPickerOpen(false);
206
+ }
207
+ break;
208
+ }
209
+ case 'ArrowDown': {
210
+ e.preventDefault();
211
+ setFocusIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0));
212
+ break;
213
+ }
214
+ case 'ArrowUp': {
215
+ e.preventDefault();
216
+ setFocusIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1));
217
+ break;
218
+ }
219
+ case 'Enter': {
220
+ e.preventDefault();
221
+ if (focusIndex >= 0 && focusIndex < items.length) {
222
+ if (compareMode) {
223
+ toggleChecked(items[focusIndex].id);
224
+ }
225
+ else {
226
+ handlePickItem(items[focusIndex].id);
227
+ }
228
+ }
229
+ break;
230
+ }
231
+ case ' ': {
232
+ if (compareMode && focusIndex >= 0 && focusIndex < items.length) {
233
+ e.preventDefault();
234
+ toggleChecked(items[focusIndex].id);
235
+ }
236
+ break;
237
+ }
238
+ default:
239
+ break;
240
+ }
241
+ }, [items, focusIndex, compareMode, toggleChecked, handlePickItem]);
242
+ // Render the picker popover via portal so it escapes ancestor overflow clipping
243
+ const pickerPopover = pickerOpen
244
+ ? react_dom_1.default.createPortal(react_1.default.createElement("div", { ref: pickerRef, style: { position: 'fixed', top: pickerPos.top, left: pickerPos.left }, className: "z-50 min-w-[180px] max-h-[200px] bg-surface border border-border rounded-lg shadow-lg flex flex-col overflow-hidden", onKeyDown: handlePickerKeyDown },
245
+ react_1.default.createElement("div", { className: "flex items-center justify-between px-2.5 py-1 border-b border-border-subtle shrink-0" }, onCompare && items.length >= 2 ? (compareMode ? (react_1.default.createElement(react_1.default.Fragment, null,
246
+ react_1.default.createElement("div", { className: "flex items-center gap-1.5" },
247
+ checkedIds.size >= 2 && (react_1.default.createElement("button", { onClick: (e) => {
248
+ e.stopPropagation();
249
+ handleCompareSelected();
250
+ }, className: "text-[10px] text-white bg-brand-primary rounded px-1.5 py-0.5 hover:bg-brand-primary/90 transition-colors font-medium" },
251
+ "Compare ",
252
+ checkedIds.size)),
253
+ react_1.default.createElement("button", { onClick: (e) => {
254
+ e.stopPropagation();
255
+ handleCompareAll();
256
+ }, className: "text-[10px] text-brand-accent hover:text-brand-primary transition-colors font-medium" }, "All")),
257
+ react_1.default.createElement("button", { onClick: (e) => {
258
+ e.stopPropagation();
259
+ setCompareMode(false);
260
+ setCheckedIds(new Set());
261
+ }, className: "text-muted hover:text-secondary transition-colors", "aria-label": "Exit compare" },
262
+ react_1.default.createElement("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2 },
263
+ react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }))))) : (react_1.default.createElement(react_1.default.Fragment, null,
264
+ react_1.default.createElement("span", { className: "text-[10px] font-medium text-muted uppercase tracking-wider" }, "Options"),
265
+ react_1.default.createElement("button", { onClick: (e) => {
266
+ e.stopPropagation();
267
+ setCompareMode(true);
268
+ }, className: "text-[10px] text-brand-accent hover:text-brand-primary transition-colors font-medium" }, "Compare\u2026")))) : (react_1.default.createElement("span", { className: "text-[10px] font-medium text-muted uppercase tracking-wider" }, "Alternates"))),
269
+ react_1.default.createElement("div", { className: "overflow-y-auto flex-1" }, items.map((item, index) => {
270
+ const isCurrent = item.id === displayedId;
271
+ const isPref = item.id === preferredId;
272
+ const isFocused = index === focusIndex;
273
+ const isChecked = compareMode && checkedIds.has(item.id);
274
+ return (react_1.default.createElement("button", { key: item.id, ref: (el) => {
275
+ itemRefs.current[index] = el;
276
+ }, onClick: (e) => {
277
+ e.stopPropagation();
278
+ if (compareMode) {
279
+ toggleChecked(item.id);
280
+ }
281
+ else {
282
+ handlePickItem(item.id);
283
+ }
284
+ }, className: `flex items-center gap-2 w-full px-2.5 py-1.5 text-left text-sm transition-colors ${isChecked
285
+ ? 'bg-selected text-brand-primary'
286
+ : isCurrent && !compareMode
287
+ ? 'bg-selected text-brand-primary font-medium'
288
+ : isFocused
289
+ ? 'bg-surface-alt text-primary'
290
+ : 'text-secondary hover:bg-hover'}` },
291
+ compareMode ? (react_1.default.createElement("span", { className: `flex items-center justify-center w-3.5 h-3.5 rounded border shrink-0 transition-colors ${isChecked
292
+ ? 'bg-brand-accent border-brand-accent text-white'
293
+ : 'border-border bg-surface'}` }, isChecked && (react_1.default.createElement("svg", { className: "w-2.5 h-2.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3 },
294
+ react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }))))) : (react_1.default.createElement("span", { className: "w-3 shrink-0 text-center text-xs" }, isPref ? react_1.default.createElement("span", { className: "text-star" }, "\u2605") : '')),
295
+ react_1.default.createElement("span", { className: "flex-1 min-w-0 truncate" },
296
+ item.label,
297
+ item.sublabel && react_1.default.createElement("span", { className: "ml-1.5 text-xs text-muted" }, item.sublabel)),
298
+ !compareMode && isCurrent && (react_1.default.createElement("svg", { className: "w-3.5 h-3.5 text-brand-accent shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3 },
299
+ react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" })))));
300
+ }))), document.body)
301
+ : null;
302
+ return (react_1.default.createElement("div", null,
303
+ label && react_1.default.createElement("div", { className: "text-xs font-semibold text-muted uppercase tracking-wider mb-1" }, label),
304
+ react_1.default.createElement("div", { className: `flex items-center gap-1.5 py-1.5 pl-0 pr-2 rounded-md ${onClick ? 'cursor-pointer hover:bg-hover transition-colors' : ''}`, onClick: handleRowClick, role: onClick ? 'button' : undefined, tabIndex: onClick ? 0 : undefined, onKeyDown: handleRowKeyDown },
305
+ react_1.default.createElement("span", { className: "w-4 shrink-0 flex items-center justify-center" }, hasAlternates ? (react_1.default.createElement("button", { ref: pickerBtnRef, onClick: handlePickerToggle, className: "text-muted hover:text-brand-accent p-0 transition-colors", "aria-label": "Switch alternate", tabIndex: -1 },
306
+ react_1.default.createElement("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2 },
307
+ react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" })))) : null),
308
+ react_1.default.createElement("span", { className: "text-sm text-primary flex-1 truncate" }, (_d = displayedItem === null || displayedItem === void 0 ? void 0 : displayedItem.label) !== null && _d !== void 0 ? _d : '',
309
+ hasAlternates && isPreferred && react_1.default.createElement("span", { className: "ml-1 text-xs text-star" }, "\u2605"),
310
+ (displayedItem === null || displayedItem === void 0 ? void 0 : displayedItem.sublabel) && (react_1.default.createElement("span", { className: "ml-1.5 text-xs text-muted" }, displayedItem.sublabel))),
311
+ rightContent,
312
+ onClick && react_1.default.createElement("span", { className: "text-faint text-xs shrink-0" }, "\u203A")),
313
+ pickerPopover));
314
+ }
315
+ //# sourceMappingURL=EntityRow.js.map
@@ -0,0 +1,50 @@
1
+ /**
2
+ * PreferredSelector — compact popover for choosing one item from a set of options.
3
+ * @packageDocumentation
4
+ */
5
+ import React from 'react';
6
+ /**
7
+ * A single selectable item in the PreferredSelector.
8
+ * @public
9
+ */
10
+ export interface ISelectableItem<TId extends string = string> {
11
+ /** Unique identifier */
12
+ readonly id: TId;
13
+ /** Primary display label */
14
+ readonly label: string;
15
+ /** Optional secondary label */
16
+ readonly sublabel?: string;
17
+ }
18
+ /**
19
+ * Props for the PreferredSelector component.
20
+ * @public
21
+ */
22
+ export interface IPreferredSelectorProps<TId extends string = string> {
23
+ /** Available items to choose from */
24
+ readonly items: ReadonlyArray<ISelectableItem<TId>>;
25
+ /** Currently selected item ID */
26
+ readonly selectedId: TId;
27
+ /** ID of the preferred/default item (shows ★ marker) */
28
+ readonly preferredId?: TId;
29
+ /** Callback when an item is selected */
30
+ readonly onSelect: (id: TId) => void;
31
+ /** Callback to compare selected items side-by-side. Receives the IDs to compare. */
32
+ readonly onCompare?: (ids: ReadonlyArray<TId>) => void;
33
+ /** Callback for drill-down navigation on an item (shows › chevron per row) */
34
+ readonly onDrillDown?: (id: TId) => void;
35
+ /** Section title displayed above the trigger */
36
+ readonly label?: string;
37
+ }
38
+ /**
39
+ * Compact inline selector with popover for choosing one item from a set.
40
+ *
41
+ * When there is only one item, renders as a static label.
42
+ * When there are multiple items, renders a clickable trigger that opens
43
+ * a popover with all options. If `onCompare` is provided, the popover
44
+ * includes a compare toggle that enables checkboxes for multi-select,
45
+ * with "Compare All" and "Compare Selected" actions.
46
+ *
47
+ * @public
48
+ */
49
+ export declare function PreferredSelector<TId extends string = string>(props: IPreferredSelectorProps<TId>): React.ReactElement;
50
+ //# sourceMappingURL=PreferredSelector.d.ts.map
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2026 Erik Fortune
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ * SOFTWARE.
22
+ */
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
35
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
36
+ }) : function(o, v) {
37
+ o["default"] = v;
38
+ });
39
+ var __importStar = (this && this.__importStar) || (function () {
40
+ var ownKeys = function(o) {
41
+ ownKeys = Object.getOwnPropertyNames || function (o) {
42
+ var ar = [];
43
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
44
+ return ar;
45
+ };
46
+ return ownKeys(o);
47
+ };
48
+ return function (mod) {
49
+ if (mod && mod.__esModule) return mod;
50
+ var result = {};
51
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
52
+ __setModuleDefault(result, mod);
53
+ return result;
54
+ };
55
+ })();
56
+ Object.defineProperty(exports, "__esModule", { value: true });
57
+ exports.PreferredSelector = PreferredSelector;
58
+ /**
59
+ * PreferredSelector — compact popover for choosing one item from a set of options.
60
+ * @packageDocumentation
61
+ */
62
+ const react_1 = __importStar(require("react"));
63
+ // ============================================================================
64
+ // PreferredSelector Component
65
+ // ============================================================================
66
+ /**
67
+ * Compact inline selector with popover for choosing one item from a set.
68
+ *
69
+ * When there is only one item, renders as a static label.
70
+ * When there are multiple items, renders a clickable trigger that opens
71
+ * a popover with all options. If `onCompare` is provided, the popover
72
+ * includes a compare toggle that enables checkboxes for multi-select,
73
+ * with "Compare All" and "Compare Selected" actions.
74
+ *
75
+ * @public
76
+ */
77
+ function PreferredSelector(props) {
78
+ var _a;
79
+ const { items, selectedId, preferredId, onSelect, onCompare, onDrillDown, label } = props;
80
+ const [open, setOpen] = (0, react_1.useState)(false);
81
+ const [focusIndex, setFocusIndex] = (0, react_1.useState)(-1);
82
+ const [compareMode, setCompareMode] = (0, react_1.useState)(false);
83
+ const [checkedIds, setCheckedIds] = (0, react_1.useState)(new Set());
84
+ const triggerRef = (0, react_1.useRef)(null);
85
+ const popoverRef = (0, react_1.useRef)(null);
86
+ const itemRefs = (0, react_1.useRef)([]);
87
+ const selectedItem = items.find((item) => item.id === selectedId);
88
+ const hasMultiple = items.length > 1;
89
+ const canCompare = onCompare !== undefined && items.length >= 2;
90
+ // Reset compare mode when popover closes
91
+ (0, react_1.useEffect)(() => {
92
+ if (!open) {
93
+ setCompareMode(false);
94
+ setCheckedIds(new Set());
95
+ }
96
+ }, [open]);
97
+ // Close popover when clicking outside
98
+ (0, react_1.useEffect)(() => {
99
+ if (!open) {
100
+ return;
101
+ }
102
+ const handleClickOutside = (e) => {
103
+ if (popoverRef.current &&
104
+ !popoverRef.current.contains(e.target) &&
105
+ triggerRef.current &&
106
+ !triggerRef.current.contains(e.target)) {
107
+ setOpen(false);
108
+ }
109
+ };
110
+ document.addEventListener('mousedown', handleClickOutside);
111
+ return () => {
112
+ document.removeEventListener('mousedown', handleClickOutside);
113
+ };
114
+ }, [open]);
115
+ // Focus first item when popover opens
116
+ (0, react_1.useEffect)(() => {
117
+ if (open) {
118
+ const selectedIdx = items.findIndex((item) => item.id === selectedId);
119
+ setFocusIndex(selectedIdx >= 0 ? selectedIdx : 0);
120
+ }
121
+ }, [open, items, selectedId]);
122
+ // Scroll focused item into view
123
+ (0, react_1.useEffect)(() => {
124
+ var _a;
125
+ if (open && focusIndex >= 0 && itemRefs.current[focusIndex]) {
126
+ (_a = itemRefs.current[focusIndex]) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: 'nearest' });
127
+ }
128
+ }, [open, focusIndex]);
129
+ const handleToggle = (0, react_1.useCallback)(() => {
130
+ if (hasMultiple) {
131
+ setOpen((prev) => !prev);
132
+ }
133
+ }, [hasMultiple]);
134
+ const handleSelect = (0, react_1.useCallback)((id) => {
135
+ onSelect(id);
136
+ setOpen(false);
137
+ }, [onSelect]);
138
+ const handleDrillDown = (0, react_1.useCallback)((id, e) => {
139
+ e.stopPropagation();
140
+ onDrillDown === null || onDrillDown === void 0 ? void 0 : onDrillDown(id);
141
+ setOpen(false);
142
+ }, [onDrillDown]);
143
+ const toggleChecked = (0, react_1.useCallback)((id) => {
144
+ setCheckedIds((prev) => {
145
+ const next = new Set(prev);
146
+ if (next.has(id)) {
147
+ next.delete(id);
148
+ }
149
+ else if (next.size < 4) {
150
+ next.add(id);
151
+ }
152
+ return next;
153
+ });
154
+ }, []);
155
+ const handleCompareAll = (0, react_1.useCallback)(() => {
156
+ if (onCompare) {
157
+ onCompare(items.map((item) => item.id));
158
+ setOpen(false);
159
+ }
160
+ }, [onCompare, items]);
161
+ const handleCompareSelected = (0, react_1.useCallback)(() => {
162
+ if (onCompare && checkedIds.size >= 2) {
163
+ onCompare(Array.from(checkedIds));
164
+ setOpen(false);
165
+ }
166
+ }, [onCompare, checkedIds]);
167
+ const handleKeyDown = (0, react_1.useCallback)((e) => {
168
+ var _a;
169
+ if (!open) {
170
+ if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
171
+ e.preventDefault();
172
+ setOpen(true);
173
+ }
174
+ return;
175
+ }
176
+ switch (e.key) {
177
+ case 'Escape': {
178
+ e.preventDefault();
179
+ e.stopPropagation();
180
+ if (compareMode) {
181
+ setCompareMode(false);
182
+ setCheckedIds(new Set());
183
+ }
184
+ else {
185
+ setOpen(false);
186
+ (_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
187
+ }
188
+ break;
189
+ }
190
+ case 'ArrowDown': {
191
+ e.preventDefault();
192
+ setFocusIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0));
193
+ break;
194
+ }
195
+ case 'ArrowUp': {
196
+ e.preventDefault();
197
+ setFocusIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1));
198
+ break;
199
+ }
200
+ case 'Enter': {
201
+ e.preventDefault();
202
+ if (focusIndex >= 0 && focusIndex < items.length) {
203
+ if (compareMode) {
204
+ toggleChecked(items[focusIndex].id);
205
+ }
206
+ else {
207
+ handleSelect(items[focusIndex].id);
208
+ }
209
+ }
210
+ break;
211
+ }
212
+ case ' ': {
213
+ if (compareMode && focusIndex >= 0 && focusIndex < items.length) {
214
+ e.preventDefault();
215
+ toggleChecked(items[focusIndex].id);
216
+ }
217
+ break;
218
+ }
219
+ default:
220
+ break;
221
+ }
222
+ }, [open, items, focusIndex, handleSelect, compareMode, toggleChecked]);
223
+ return (react_1.default.createElement("div", { className: "relative" },
224
+ label && react_1.default.createElement("div", { className: "text-xs font-medium text-muted mb-1" }, label),
225
+ react_1.default.createElement("button", { ref: triggerRef, onClick: handleToggle, onKeyDown: handleKeyDown, className: `flex items-center gap-1.5 px-2 py-1 text-sm rounded-md border transition-colors w-full text-left ${hasMultiple
226
+ ? 'border-border hover:border-brand-primary cursor-pointer bg-surface'
227
+ : 'border-transparent cursor-default bg-transparent'} ${open ? 'border-brand-primary ring-1 ring-focus-ring/20' : ''}`, "aria-expanded": open, "aria-haspopup": "listbox" },
228
+ react_1.default.createElement("span", { className: "flex-1 min-w-0 truncate text-primary" }, (_a = selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.label) !== null && _a !== void 0 ? _a : selectedId,
229
+ preferredId !== undefined && selectedId === preferredId && (react_1.default.createElement("span", { className: "ml-1 text-xs text-star" }, "\u2605"))),
230
+ hasMultiple && (react_1.default.createElement(react_1.default.Fragment, null,
231
+ react_1.default.createElement("span", { className: "text-[10px] text-muted shrink-0 tabular-nums" }, items.length),
232
+ react_1.default.createElement("svg", { className: `w-3 h-3 text-muted shrink-0 transition-transform ${open ? 'rotate-180' : ''}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2 },
233
+ react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }))))),
234
+ open && (react_1.default.createElement("div", { ref: popoverRef, className: "absolute z-50 mt-1 w-full min-w-[200px] max-h-[280px] bg-surface border border-border rounded-lg shadow-lg flex flex-col overflow-hidden", role: "listbox", onKeyDown: handleKeyDown },
235
+ canCompare && (react_1.default.createElement("div", { className: "flex items-center justify-between px-2.5 py-1.5 border-b border-border-subtle shrink-0" },
236
+ compareMode ? (react_1.default.createElement("div", { className: "flex items-center gap-1.5" },
237
+ checkedIds.size >= 2 && (react_1.default.createElement("button", { onClick: handleCompareSelected, className: "text-xs text-white bg-brand-primary rounded px-1.5 py-0.5 hover:bg-brand-primary/90 transition-colors font-medium" },
238
+ "Compare ",
239
+ checkedIds.size)),
240
+ react_1.default.createElement("button", { onClick: handleCompareAll, className: "text-xs text-brand-accent hover:text-brand-primary transition-colors font-medium" }, "All"))) : (react_1.default.createElement("button", { onClick: () => setCompareMode(true), className: "text-xs text-brand-accent hover:text-brand-primary transition-colors font-medium" }, "Compare\u2026")),
241
+ react_1.default.createElement("button", { onClick: () => {
242
+ if (compareMode) {
243
+ setCompareMode(false);
244
+ setCheckedIds(new Set());
245
+ }
246
+ else {
247
+ setOpen(false);
248
+ }
249
+ }, className: "text-muted hover:text-secondary transition-colors", "aria-label": compareMode ? 'Exit compare' : 'Close' },
250
+ react_1.default.createElement("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2 },
251
+ react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }))))),
252
+ react_1.default.createElement("div", { className: "overflow-y-auto flex-1" }, items.map((item, index) => {
253
+ const isSelected = item.id === selectedId;
254
+ const isPreferred = preferredId !== undefined && item.id === preferredId;
255
+ const isFocused = index === focusIndex;
256
+ const isChecked = compareMode && checkedIds.has(item.id);
257
+ return (react_1.default.createElement("button", { key: item.id, ref: (el) => {
258
+ itemRefs.current[index] = el;
259
+ }, role: "option", "aria-selected": isSelected, onClick: () => {
260
+ if (compareMode) {
261
+ toggleChecked(item.id);
262
+ }
263
+ else {
264
+ handleSelect(item.id);
265
+ }
266
+ }, className: `flex items-center gap-2 w-full px-2.5 py-1.5 text-left text-sm transition-colors ${isChecked
267
+ ? 'bg-selected text-brand-primary'
268
+ : isSelected && !compareMode
269
+ ? 'bg-selected text-brand-primary font-medium'
270
+ : isFocused
271
+ ? 'bg-surface-alt text-primary'
272
+ : 'text-secondary hover:bg-hover'}` },
273
+ compareMode ? (react_1.default.createElement("span", { className: `flex items-center justify-center w-3.5 h-3.5 rounded border shrink-0 transition-colors ${isChecked
274
+ ? 'bg-brand-accent border-brand-accent text-white'
275
+ : 'border-border bg-surface'}` }, isChecked && (react_1.default.createElement("svg", { className: "w-2.5 h-2.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3 },
276
+ react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }))))) : (react_1.default.createElement("span", { className: "w-3 shrink-0 text-center text-xs" }, isPreferred ? react_1.default.createElement("span", { className: "text-star" }, "\u2605") : '')),
277
+ react_1.default.createElement("span", { className: "flex-1 min-w-0" },
278
+ react_1.default.createElement("span", { className: "truncate block" }, item.label),
279
+ item.sublabel && (react_1.default.createElement("span", { className: "text-xs text-muted truncate block" }, item.sublabel))),
280
+ !compareMode && isSelected && (react_1.default.createElement("svg", { className: "w-3.5 h-3.5 text-brand-accent shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3 },
281
+ react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }))),
282
+ !compareMode && onDrillDown && (react_1.default.createElement("button", { onClick: (e) => handleDrillDown(item.id, e), className: "text-faint hover:text-brand-accent shrink-0 p-0.5 -mr-1", "aria-label": `Open ${item.label}` },
283
+ react_1.default.createElement("svg", { className: "w-3 h-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2 },
284
+ react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }))))));
285
+ }))))));
286
+ }
287
+ //# sourceMappingURL=PreferredSelector.js.map
@@ -0,0 +1,5 @@
1
+ export { EntityRow } from './EntityRow';
2
+ export type { IEntityRowProps } from './EntityRow';
3
+ export { PreferredSelector } from './PreferredSelector';
4
+ export type { IPreferredSelectorProps, ISelectableItem } from './PreferredSelector';
5
+ //# sourceMappingURL=index.d.ts.map