fluentui-extended 2026.6.12 → 2026.8.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +236 -17
- package/dist/index.d.mts +19 -6
- package/dist/index.d.ts +19 -6
- package/dist/index.js +48 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +48 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ An Advanced Find-style query builder for Dynamics 365. Build complex filter cond
|
|
|
38
38
|
## Quick Start
|
|
39
39
|
|
|
40
40
|
```tsx
|
|
41
|
+
import { useState } from 'react';
|
|
41
42
|
import { Lookup, LookupOption } from 'fluentui-extended';
|
|
42
43
|
import { FluentProvider, webLightTheme } from '@fluentui/react-components';
|
|
43
44
|
|
|
@@ -161,6 +162,113 @@ import { AddRegular, PersonSearchRegular } from '@fluentui/react-icons';
|
|
|
161
162
|
/>
|
|
162
163
|
```
|
|
163
164
|
|
|
165
|
+
### Multi-Entity Lookup with Filter Buttons
|
|
166
|
+
|
|
167
|
+
Replicate the native Dynamics 365 lookup pattern where users can filter between entity types:
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import { Text, Button, Link, ToggleButton } from '@fluentui/react-components';
|
|
171
|
+
import { BuildingRegular, PersonRegular, ArrowLeftRegular } from '@fluentui/react-icons';
|
|
172
|
+
|
|
173
|
+
function MultiEntityLookup() {
|
|
174
|
+
const [showAccounts, setShowAccounts] = useState(true);
|
|
175
|
+
const [showContacts, setShowContacts] = useState(true);
|
|
176
|
+
|
|
177
|
+
const options: LookupOption[] = [
|
|
178
|
+
{ key: 'acc-1', text: 'Contoso Ltd', icon: <BuildingRegular />, details: [...] },
|
|
179
|
+
{ key: 'con-1', text: 'John Smith', icon: <PersonRegular />, details: [...] },
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
// Filter based on key prefix
|
|
183
|
+
const filteredOptions = useMemo(() =>
|
|
184
|
+
options.filter(opt => {
|
|
185
|
+
if (opt.key.startsWith('acc-')) return showAccounts;
|
|
186
|
+
if (opt.key.startsWith('con-')) return showContacts;
|
|
187
|
+
return true;
|
|
188
|
+
}), [showAccounts, showContacts]
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<Lookup
|
|
193
|
+
options={filteredOptions}
|
|
194
|
+
header={
|
|
195
|
+
// Drill-down view when single entity selected
|
|
196
|
+
showAccounts !== showContacts ? (
|
|
197
|
+
<>
|
|
198
|
+
<Link onClick={() => { setShowAccounts(true); setShowContacts(true); }}>
|
|
199
|
+
<ArrowLeftRegular /> All
|
|
200
|
+
</Link>
|
|
201
|
+
<Text weight="semibold">{showAccounts ? 'Accounts' : 'Contacts'}</Text>
|
|
202
|
+
</>
|
|
203
|
+
) : (
|
|
204
|
+
// Filter toggles when showing all
|
|
205
|
+
<>
|
|
206
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
|
207
|
+
<Text size={200}>Results from:</Text>
|
|
208
|
+
<ToggleButton size="small" checked={showAccounts}
|
|
209
|
+
onClick={() => { setShowAccounts(true); setShowContacts(false); }}>
|
|
210
|
+
Accounts
|
|
211
|
+
</ToggleButton>
|
|
212
|
+
<ToggleButton size="small" checked={showContacts}
|
|
213
|
+
onClick={() => { setShowAccounts(false); setShowContacts(true); }}>
|
|
214
|
+
Contacts
|
|
215
|
+
</ToggleButton>
|
|
216
|
+
</div>
|
|
217
|
+
<Button size="small">Recent records</Button>
|
|
218
|
+
</>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
/>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Rich Secondary Text with React Elements
|
|
227
|
+
|
|
228
|
+
Both `secondaryText` and `details` support React elements, not just strings:
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { Badge } from '@fluentui/react-components';
|
|
232
|
+
import { CheckmarkCircleRegular } from '@fluentui/react-icons';
|
|
233
|
+
|
|
234
|
+
const options: LookupOption[] = [
|
|
235
|
+
{
|
|
236
|
+
key: '1',
|
|
237
|
+
text: 'John Smith',
|
|
238
|
+
secondaryText: (
|
|
239
|
+
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
240
|
+
<Badge appearance="tint" color="success" size="small" icon={<CheckmarkCircleRegular />}>
|
|
241
|
+
Active
|
|
242
|
+
</Badge>
|
|
243
|
+
<span>john.smith@contoso.com</span>
|
|
244
|
+
</span>
|
|
245
|
+
),
|
|
246
|
+
icon: <PersonRegular />,
|
|
247
|
+
details: [
|
|
248
|
+
{ label: 'Status', value: <Badge appearance="filled" color="success" size="small">Verified</Badge> },
|
|
249
|
+
{ label: 'Role', value: <Badge appearance="tint" color="brand" size="small">Decision Maker</Badge> },
|
|
250
|
+
{ value: <span style={{ color: '#0078d4' }}>View full profile →</span> },
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Test Harness Examples
|
|
259
|
+
|
|
260
|
+
Run the test harness with `npm run harness` to see all examples in action.
|
|
261
|
+
|
|
262
|
+
| Example | Description |
|
|
263
|
+
|---------|-------------|
|
|
264
|
+
| **Basic Lookup** | Simple lookup with expandable details, no header/footer |
|
|
265
|
+
| **Header & Footer** | D365-style with "Accounts" header and "New" / "Advanced" footer links |
|
|
266
|
+
| **Details Only (No Secondary Text)** | Options with icons + details but no secondary text; demonstrates icon centering on single-line text |
|
|
267
|
+
| **Multi-Entity Filter** | Toggle between Accounts/Contacts with drill-down header pattern (← All) |
|
|
268
|
+
| **Dynamic Search (Async API)** | Simulated 800ms API delay with loading spinner; auto-loads top 5 on open |
|
|
269
|
+
| **Live Dynamics Lookup** | Connects to real D365 environment via Xrm.WebApi (when connected) |
|
|
270
|
+
| **QueryBuilder** | Full Advanced Find-style query builder with FetchXML serialization |
|
|
271
|
+
|
|
164
272
|
## API Reference
|
|
165
273
|
|
|
166
274
|
### Lookup Props
|
|
@@ -185,6 +293,45 @@ import { AddRegular, PersonSearchRegular } from '@fluentui/react-icons';
|
|
|
185
293
|
| `disabled` | `boolean` | `false` | Disable the lookup |
|
|
186
294
|
| `open` | `boolean` | - | Controlled open state for the dropdown |
|
|
187
295
|
| `onOpenChange` | `(open: boolean) => void` | - | Callback when dropdown open state changes |
|
|
296
|
+
| `disableClientFilter` | `boolean` | `false` | Disable client-side filtering of options. Use this when filtering is performed server-side via `onSearchChange` |
|
|
297
|
+
| `searchFields` | `string` | - | Hidden searchable text (never rendered). Use this to include additional searchable content (codes, IDs) while displaying JSX in `secondaryText` |
|
|
298
|
+
|
|
299
|
+
### Client-Side Filtering
|
|
300
|
+
|
|
301
|
+
By default, the Lookup component filters options client-side as the user types. The filtering logic works as follows:
|
|
302
|
+
|
|
303
|
+
1. **Primary field (`text`)** — Always searched, regardless of other props
|
|
304
|
+
2. **Search fields (`searchFields`)** — If provided, this hidden text is searched (useful when `secondaryText` is JSX)
|
|
305
|
+
3. **Secondary text (`secondaryText`)** — Only searched if it's a string (JSX elements are skipped)
|
|
306
|
+
|
|
307
|
+
This allows you to use rich JSX (badges, icons) in `secondaryText` while still providing searchable text via `searchFields`:
|
|
308
|
+
|
|
309
|
+
```tsx
|
|
310
|
+
const options: LookupOption[] = [
|
|
311
|
+
{
|
|
312
|
+
key: 'PROD-001',
|
|
313
|
+
text: 'Acme Widget', // Always searchable
|
|
314
|
+
searchFields: 'PROD-001 SKU-12345 acme-widget', // Hidden searchable text
|
|
315
|
+
secondaryText: ( // Rich display (not searchable)
|
|
316
|
+
<span style={{ display: 'flex', gap: 4 }}>
|
|
317
|
+
<Badge size="small">PROD-001</Badge>
|
|
318
|
+
<Badge size="small" color="brand">SKU-12345</Badge>
|
|
319
|
+
</span>
|
|
320
|
+
),
|
|
321
|
+
},
|
|
322
|
+
];
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Server-Side Filtering:** When using `onSearchChange` to fetch results from an API, set `disableClientFilter={true}` to prevent the client from re-filtering server results:
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
<Lookup
|
|
329
|
+
options={apiResults}
|
|
330
|
+
onSearchChange={(searchText) => fetchFromApi(searchText)}
|
|
331
|
+
disableClientFilter={true} // API already filtered the results
|
|
332
|
+
loading={isLoading}
|
|
333
|
+
/>
|
|
334
|
+
```
|
|
188
335
|
|
|
189
336
|
### Cross-Document Support (Dynamics 365 Iframes)
|
|
190
337
|
|
|
@@ -216,21 +363,24 @@ The Lookup component extends Fluent UI's `Input` and supports these standard pro
|
|
|
216
363
|
|
|
217
364
|
```ts
|
|
218
365
|
interface LookupOption {
|
|
219
|
-
key: string;
|
|
220
|
-
text: string;
|
|
221
|
-
secondaryText?:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
366
|
+
key: string; // Unique identifier (required)
|
|
367
|
+
text: string; // Display text (required)
|
|
368
|
+
secondaryText?: ReactNode; // Secondary line - string, Badge, or JSX
|
|
369
|
+
searchFields?: string; // Hidden searchable text (never rendered)
|
|
370
|
+
icon?: ReactNode; // Icon component (e.g., <BuildingRegular />)
|
|
371
|
+
details?: LookupOptionDetail[]; // Expandable details (chevron appears)
|
|
372
|
+
data?: unknown; // Custom data payload for your app
|
|
373
|
+
disabled?: boolean; // Disable this option
|
|
226
374
|
}
|
|
227
375
|
|
|
228
376
|
interface LookupOptionDetail {
|
|
229
|
-
label?:
|
|
230
|
-
value:
|
|
377
|
+
label?: ReactNode; // Optional label (e.g., "Phone:" or a Badge)
|
|
378
|
+
value: ReactNode; // Detail value - string or JSX element
|
|
231
379
|
}
|
|
232
380
|
```
|
|
233
381
|
|
|
382
|
+
> **Note:** Both `secondaryText` and `details` support React elements, not just strings. See [Rich Secondary Text](#rich-secondary-text-with-react-elements) for examples. When using JSX in `secondaryText`, use `searchFields` to provide hidden searchable text (see [Client-Side Filtering](#client-side-filtering)).
|
|
383
|
+
|
|
234
384
|
## Keyboard Navigation
|
|
235
385
|
|
|
236
386
|
| Key | Action |
|
|
@@ -526,14 +676,6 @@ if (!result.isValid) {
|
|
|
526
676
|
|
|
527
677
|
---
|
|
528
678
|
|
|
529
|
-
## Contributing
|
|
530
|
-
|
|
531
|
-
1. Fork the repository
|
|
532
|
-
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
533
|
-
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
534
|
-
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
535
|
-
5. Open a Pull Request
|
|
536
|
-
|
|
537
679
|
## Acknowledgments
|
|
538
680
|
|
|
539
681
|
This library extends [Microsoft's Fluent UI React v9](https://react.fluentui.dev/) components. Thank you to Microsoft and the Fluent UI team for creating and maintaining such an excellent design system.
|
|
@@ -542,6 +684,83 @@ This library extends [Microsoft's Fluent UI React v9](https://react.fluentui.dev
|
|
|
542
684
|
- [Fluent UI GitHub](https://github.com/microsoft/fluentui)
|
|
543
685
|
- [Fluent 2 Design System](https://fluent2.microsoft.design/)
|
|
544
686
|
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
## Changelog
|
|
690
|
+
|
|
691
|
+
> Version format: `YYYY.M.DD` (e.g., `2026.8.30` = August 30, 2026)
|
|
692
|
+
|
|
693
|
+
### 2026.8.30
|
|
694
|
+
|
|
695
|
+
- ✨ Added multi-entity filter pattern with drill-down header ("← All" back button) in test harness
|
|
696
|
+
- ✨ Added "Details Only (No Secondary Text)" example to test harness
|
|
697
|
+
- ✨ Support for React elements in `secondaryText` and `details[].value` (Badges, icons, styled text)
|
|
698
|
+
- ♻️ Improved `aria-selected`/`aria-disabled` attribute handling
|
|
699
|
+
- 📝 Documentation overhaul with complete API reference and examples
|
|
700
|
+
|
|
701
|
+
### 2026.6.12
|
|
702
|
+
|
|
703
|
+
- ✨ Added React 19 support to peerDependencies
|
|
704
|
+
- 📝 Updated README with cross-document support documentation
|
|
705
|
+
|
|
706
|
+
### 2026.2.19
|
|
707
|
+
|
|
708
|
+
- 🐛 Fixed cross-document dismiss in Dynamics 365 iframes using `ownerDocument`
|
|
709
|
+
|
|
710
|
+
### 2026.2.17
|
|
711
|
+
|
|
712
|
+
- 🐛 Removed `requestAnimationFrame` — handler registers immediately
|
|
713
|
+
- 🐛 Switched to capture phase for dismiss handler to prevent D365 DOM interference
|
|
714
|
+
|
|
715
|
+
### 2026.2.15
|
|
716
|
+
|
|
717
|
+
- ✨ Added controlled `open` and `onOpenChange` props for programmatic dropdown control
|
|
718
|
+
- 🐛 Removed `requestAnimationFrame` from dismiss handler
|
|
719
|
+
|
|
720
|
+
### 2026.2.13
|
|
721
|
+
|
|
722
|
+
- ♻️ Rebuilt popup from scratch using custom element (Fluent UI Popover had unexpected behavior)
|
|
723
|
+
|
|
724
|
+
### 2026.2.11
|
|
725
|
+
|
|
726
|
+
- 🐛 Added `onOpenChange` callback to sync internal state with Popover dismiss events (outside click, Escape, focus loss)
|
|
727
|
+
|
|
728
|
+
### 2026.2.10
|
|
729
|
+
|
|
730
|
+
- 🐛 Fixed Lookup not allowing space character in search input
|
|
731
|
+
|
|
732
|
+
### 2026.2.8
|
|
733
|
+
|
|
734
|
+
- ✨ **QueryBuilder**: Native API integration for field metadata
|
|
735
|
+
- ✨ **QueryBuilder**: Lookup field support with related entity validation
|
|
736
|
+
|
|
737
|
+
### 2026.2.7
|
|
738
|
+
|
|
739
|
+
- ♻️ Removed `Xrm` global dependency — now uses native `/api/data/v9.2/` fetch calls
|
|
740
|
+
|
|
741
|
+
### 2026.2.6
|
|
742
|
+
|
|
743
|
+
- ♻️ **QueryBuilder**: Refactored to reuse shared components and styles
|
|
744
|
+
|
|
745
|
+
### 2026.2.5
|
|
746
|
+
|
|
747
|
+
- ✨ **QueryBuilder**: Initial release (beta) — Advanced Find-style query builder
|
|
748
|
+
- ♻️ **Lookup**: Changed from options-only to popup-based rendering
|
|
749
|
+
|
|
750
|
+
### 2026.2.3
|
|
751
|
+
|
|
752
|
+
- ✨ Added `id` prop — auto-generated if not provided
|
|
753
|
+
|
|
754
|
+
### 2026.2.2
|
|
755
|
+
|
|
756
|
+
- 🐛 Fixed classic JSX transform for React 16 compatibility
|
|
757
|
+
- ✨ Added React 16.8+ support
|
|
758
|
+
|
|
759
|
+
### 2026.2.1
|
|
760
|
+
|
|
761
|
+
- 🎉 Initial release
|
|
762
|
+
- ✨ Lookup component with async search, expandable details, header/footer
|
|
763
|
+
|
|
545
764
|
## License
|
|
546
765
|
|
|
547
766
|
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -2,18 +2,25 @@ import * as React from 'react';
|
|
|
2
2
|
import { InputProps } from '@fluentui/react-components';
|
|
3
3
|
|
|
4
4
|
interface LookupOptionDetail {
|
|
5
|
-
/** Label for the detail row */
|
|
6
|
-
label?:
|
|
7
|
-
/** Value for the detail row */
|
|
8
|
-
value:
|
|
5
|
+
/** Label for the detail row - can be a string or React element */
|
|
6
|
+
label?: React.ReactNode;
|
|
7
|
+
/** Value for the detail row - can be a string or React element */
|
|
8
|
+
value: React.ReactNode;
|
|
9
9
|
}
|
|
10
10
|
interface LookupOption {
|
|
11
11
|
/** Unique identifier for the option */
|
|
12
12
|
key: string;
|
|
13
13
|
/** Display text for the option */
|
|
14
14
|
text: string;
|
|
15
|
-
/** Optional secondary text */
|
|
16
|
-
secondaryText?:
|
|
15
|
+
/** Optional secondary text - can be a string or React element */
|
|
16
|
+
secondaryText?: React.ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* Optional searchable text that is never rendered. Use this to include
|
|
19
|
+
* additional searchable content (codes, IDs, etc.) without affecting display.
|
|
20
|
+
* Client-side filtering will search this field in addition to `text` and
|
|
21
|
+
* string `secondaryText`.
|
|
22
|
+
*/
|
|
23
|
+
searchFields?: string;
|
|
17
24
|
/** Optional icon to display */
|
|
18
25
|
icon?: React.ReactNode;
|
|
19
26
|
/** Optional expandable details */
|
|
@@ -64,6 +71,12 @@ interface LookupProps extends Omit<InputProps, 'onChange' | 'value'> {
|
|
|
64
71
|
* Use together with `open` for controlled mode, or standalone to observe changes.
|
|
65
72
|
*/
|
|
66
73
|
onOpenChange?: (open: boolean) => void;
|
|
74
|
+
/**
|
|
75
|
+
* Disable client-side filtering of options. Use this when filtering is
|
|
76
|
+
* performed server-side via `onSearchChange` and the returned options are
|
|
77
|
+
* already filtered. Defaults to `false` (client-side filtering enabled).
|
|
78
|
+
*/
|
|
79
|
+
disableClientFilter?: boolean;
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
declare const Lookup: React.FC<LookupProps>;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,18 +2,25 @@ import * as React from 'react';
|
|
|
2
2
|
import { InputProps } from '@fluentui/react-components';
|
|
3
3
|
|
|
4
4
|
interface LookupOptionDetail {
|
|
5
|
-
/** Label for the detail row */
|
|
6
|
-
label?:
|
|
7
|
-
/** Value for the detail row */
|
|
8
|
-
value:
|
|
5
|
+
/** Label for the detail row - can be a string or React element */
|
|
6
|
+
label?: React.ReactNode;
|
|
7
|
+
/** Value for the detail row - can be a string or React element */
|
|
8
|
+
value: React.ReactNode;
|
|
9
9
|
}
|
|
10
10
|
interface LookupOption {
|
|
11
11
|
/** Unique identifier for the option */
|
|
12
12
|
key: string;
|
|
13
13
|
/** Display text for the option */
|
|
14
14
|
text: string;
|
|
15
|
-
/** Optional secondary text */
|
|
16
|
-
secondaryText?:
|
|
15
|
+
/** Optional secondary text - can be a string or React element */
|
|
16
|
+
secondaryText?: React.ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* Optional searchable text that is never rendered. Use this to include
|
|
19
|
+
* additional searchable content (codes, IDs, etc.) without affecting display.
|
|
20
|
+
* Client-side filtering will search this field in addition to `text` and
|
|
21
|
+
* string `secondaryText`.
|
|
22
|
+
*/
|
|
23
|
+
searchFields?: string;
|
|
17
24
|
/** Optional icon to display */
|
|
18
25
|
icon?: React.ReactNode;
|
|
19
26
|
/** Optional expandable details */
|
|
@@ -64,6 +71,12 @@ interface LookupProps extends Omit<InputProps, 'onChange' | 'value'> {
|
|
|
64
71
|
* Use together with `open` for controlled mode, or standalone to observe changes.
|
|
65
72
|
*/
|
|
66
73
|
onOpenChange?: (open: boolean) => void;
|
|
74
|
+
/**
|
|
75
|
+
* Disable client-side filtering of options. Use this when filtering is
|
|
76
|
+
* performed server-side via `onSearchChange` and the returned options are
|
|
77
|
+
* already filtered. Defaults to `false` (client-side filtering enabled).
|
|
78
|
+
*/
|
|
79
|
+
disableClientFilter?: boolean;
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
declare const Lookup: React.FC<LookupProps>;
|
package/dist/index.js
CHANGED
|
@@ -88,15 +88,19 @@ var useLookupStyles = reactComponents.makeStyles({
|
|
|
88
88
|
flexDirection: "row",
|
|
89
89
|
alignItems: "flex-start",
|
|
90
90
|
boxSizing: "border-box",
|
|
91
|
-
|
|
91
|
+
paddingTop: "6px",
|
|
92
|
+
paddingBottom: "6px",
|
|
93
|
+
paddingLeft: "12px",
|
|
94
|
+
paddingRight: "8px",
|
|
92
95
|
cursor: "pointer",
|
|
93
|
-
borderRadius: reactComponents.tokens.
|
|
96
|
+
borderRadius: reactComponents.tokens.borderRadiusNone,
|
|
94
97
|
backgroundColor: "transparent",
|
|
95
98
|
border: "none",
|
|
96
99
|
width: "100%",
|
|
97
100
|
overflow: "hidden",
|
|
98
101
|
textAlign: "left",
|
|
99
|
-
gap: "
|
|
102
|
+
gap: "10px",
|
|
103
|
+
minHeight: "40px",
|
|
100
104
|
"&:hover": {
|
|
101
105
|
backgroundColor: reactComponents.tokens.colorNeutralBackground1Hover
|
|
102
106
|
},
|
|
@@ -110,16 +114,24 @@ var useLookupStyles = reactComponents.makeStyles({
|
|
|
110
114
|
display: "flex",
|
|
111
115
|
alignItems: "center",
|
|
112
116
|
justifyContent: "center",
|
|
113
|
-
width: "
|
|
114
|
-
height: "
|
|
117
|
+
width: "24px",
|
|
118
|
+
height: "24px",
|
|
119
|
+
fontSize: "20px",
|
|
115
120
|
flexShrink: 0,
|
|
116
|
-
color: reactComponents.tokens.
|
|
121
|
+
color: reactComponents.tokens.colorNeutralForeground1,
|
|
122
|
+
marginTop: "0px"
|
|
123
|
+
// Centered on single text row
|
|
124
|
+
},
|
|
125
|
+
optionIconWithSecondary: {
|
|
126
|
+
marginTop: "6px"
|
|
127
|
+
// Centered between text + secondaryText rows
|
|
117
128
|
},
|
|
118
129
|
optionContent: {
|
|
119
130
|
display: "flex",
|
|
120
131
|
flexDirection: "column",
|
|
121
132
|
flex: "1 1 auto",
|
|
122
|
-
minWidth: 0
|
|
133
|
+
minWidth: 0,
|
|
134
|
+
gap: "2px"
|
|
123
135
|
},
|
|
124
136
|
optionExpandButton: {
|
|
125
137
|
display: "flex",
|
|
@@ -128,6 +140,7 @@ var useLookupStyles = reactComponents.makeStyles({
|
|
|
128
140
|
width: "24px",
|
|
129
141
|
height: "24px",
|
|
130
142
|
marginLeft: "auto",
|
|
143
|
+
marginTop: "2px",
|
|
131
144
|
flexShrink: 0,
|
|
132
145
|
cursor: "pointer",
|
|
133
146
|
borderRadius: reactComponents.tokens.borderRadiusSmall,
|
|
@@ -161,14 +174,15 @@ var useLookupStyles = reactComponents.makeStyles({
|
|
|
161
174
|
},
|
|
162
175
|
optionText: {
|
|
163
176
|
fontSize: reactComponents.tokens.fontSizeBase300,
|
|
164
|
-
fontWeight: reactComponents.tokens.
|
|
165
|
-
color: reactComponents.tokens.colorNeutralForeground1
|
|
177
|
+
fontWeight: reactComponents.tokens.fontWeightSemibold,
|
|
178
|
+
color: reactComponents.tokens.colorNeutralForeground1,
|
|
179
|
+
lineHeight: reactComponents.tokens.lineHeightBase300
|
|
166
180
|
},
|
|
167
181
|
optionSecondaryText: {
|
|
168
182
|
fontSize: reactComponents.tokens.fontSizeBase200,
|
|
169
183
|
fontWeight: reactComponents.tokens.fontWeightRegular,
|
|
170
|
-
color: reactComponents.tokens.
|
|
171
|
-
|
|
184
|
+
color: reactComponents.tokens.colorNeutralForeground2,
|
|
185
|
+
lineHeight: reactComponents.tokens.lineHeightBase200
|
|
172
186
|
},
|
|
173
187
|
optionDetails: {
|
|
174
188
|
display: "flex",
|
|
@@ -267,6 +281,7 @@ var Lookup = ({
|
|
|
267
281
|
footer,
|
|
268
282
|
open: controlledOpen,
|
|
269
283
|
onOpenChange,
|
|
284
|
+
disableClientFilter = false,
|
|
270
285
|
...inputProps
|
|
271
286
|
}) => {
|
|
272
287
|
const styles = useLookupStyles();
|
|
@@ -302,14 +317,26 @@ var Lookup = ({
|
|
|
302
317
|
[selectedOptionProp, options, selectedKey, internalSelectedOption]
|
|
303
318
|
);
|
|
304
319
|
const filteredOptions = React3__namespace.useMemo(() => {
|
|
320
|
+
if (disableClientFilter) {
|
|
321
|
+
return options;
|
|
322
|
+
}
|
|
305
323
|
if (!searchText || searchText.length < minSearchLength) {
|
|
306
324
|
return options;
|
|
307
325
|
}
|
|
308
326
|
const lowerSearch = searchText.toLowerCase();
|
|
309
|
-
return options.filter(
|
|
310
|
-
(opt
|
|
311
|
-
|
|
312
|
-
|
|
327
|
+
return options.filter((opt) => {
|
|
328
|
+
if (opt.text.toLowerCase().includes(lowerSearch)) {
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
if (opt.searchFields && opt.searchFields.toLowerCase().includes(lowerSearch)) {
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
if (typeof opt.secondaryText === "string") {
|
|
335
|
+
return opt.secondaryText.toLowerCase().includes(lowerSearch);
|
|
336
|
+
}
|
|
337
|
+
return false;
|
|
338
|
+
});
|
|
339
|
+
}, [options, searchText, minSearchLength, disableClientFilter]);
|
|
313
340
|
const highlightedOptionId = React3__namespace.useMemo(() => {
|
|
314
341
|
if (!isOpen || highlightedIndex < 0 || highlightedIndex >= filteredOptions.length) {
|
|
315
342
|
return void 0;
|
|
@@ -580,8 +607,8 @@ var Lookup = ({
|
|
|
580
607
|
id: `${lookupId}-option-${option.key}`,
|
|
581
608
|
role: "option",
|
|
582
609
|
"data-index": index,
|
|
583
|
-
"aria-selected": option.key === selectedOption?.key
|
|
584
|
-
"aria-disabled": option.disabled
|
|
610
|
+
"aria-selected": option.key === selectedOption?.key,
|
|
611
|
+
"aria-disabled": option.disabled || void 0,
|
|
585
612
|
className: reactComponents.mergeClasses(
|
|
586
613
|
styles.option,
|
|
587
614
|
index === highlightedIndex && styles.optionHighlighted,
|
|
@@ -591,7 +618,10 @@ var Lookup = ({
|
|
|
591
618
|
onClick: () => handleSelectOption(option),
|
|
592
619
|
onMouseEnter: () => setHighlightedIndex(index)
|
|
593
620
|
},
|
|
594
|
-
option.icon && /* @__PURE__ */ React3__namespace.createElement("span", { className:
|
|
621
|
+
option.icon && /* @__PURE__ */ React3__namespace.createElement("span", { className: reactComponents.mergeClasses(
|
|
622
|
+
styles.optionIcon,
|
|
623
|
+
!!option.secondaryText && styles.optionIconWithSecondary
|
|
624
|
+
) }, option.icon),
|
|
595
625
|
/* @__PURE__ */ React3__namespace.createElement("span", { className: styles.optionContent }, /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.optionText }, option.text), option.secondaryText && /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.optionSecondaryText }, option.secondaryText), isExpanded && hasDetails && /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.optionDetails }, option.details.map((detail, detailIndex) => /* @__PURE__ */ React3__namespace.createElement("div", { key: detailIndex, className: styles.optionDetailRow }, detail.label && /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.optionDetailLabel }, detail.label, ":"), /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.optionDetailValue }, detail.value))))),
|
|
596
626
|
hasDetails && /* @__PURE__ */ React3__namespace.createElement(
|
|
597
627
|
"span",
|