@medplum/react 5.1.27 → 5.1.29

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.
@@ -63,6 +63,7 @@ import type { MedicationRequest } from '@medplum/fhirtypes';
63
63
  import type { MedicationSearchParams } from '@medplum/core';
64
64
  import type { MedplumClient } from '@medplum/core';
65
65
  import { MemoExoticComponent } from 'react';
66
+ import type { ModalProps as ModalProps_2 } from '@mantine/core';
66
67
  import type { Money } from '@medplum/fhirtypes';
67
68
  import type { MouseEvent as MouseEvent_2 } from 'react';
68
69
  import type { MouseEventHandler } from 'react';
@@ -114,6 +115,7 @@ import type { SearchRequest } from '@medplum/core';
114
115
  import type { ServiceRequest } from '@medplum/fhirtypes';
115
116
  import type { Signature } from '@medplum/fhirtypes';
116
117
  import type { Slot } from '@medplum/fhirtypes';
118
+ import type { SpotlightActionData } from '@mantine/spotlight';
117
119
  import type { StructureDefinition } from '@medplum/fhirtypes';
118
120
  import type { Subscription } from '@medplum/fhirtypes';
119
121
  import type { SyntheticEvent } from 'react';
@@ -251,6 +253,7 @@ export declare interface AIRealTimeQuestionnaireFormProps extends QuestionnaireF
251
253
  readonly aiModel?: string;
252
254
  readonly onTranscript?: (fullTranscript: string, chunk: string) => void;
253
255
  readonly voiceInstructions?: ReactNode;
256
+ readonly silenceDebounceMs?: number;
254
257
  }
255
258
 
256
259
  /** Allergies section — searches for AllergyIntolerance resources. */
@@ -261,6 +264,23 @@ export declare function AnnotationInput(props: AnnotationInputProps): JSX.Elemen
261
264
  export declare interface AnnotationInputProps extends ComplexTypeInputProps<Annotation> {
262
265
  }
263
266
 
267
+ /**
268
+ * Applies the `questionnaire-optionExclusive` rule to an answer change.
269
+ *
270
+ * Given the previous and newly requested answers for an item, enforces that selecting an answer
271
+ * option marked exclusive clears every other answer, and selecting any other option clears a
272
+ * previously selected exclusive answer. Returns the new answers unchanged if the item has no
273
+ * exclusive options or the change only removed answers.
274
+ *
275
+ * See: https://hl7.org/fhir/extensions/StructureDefinition-questionnaire-optionExclusive.html
276
+ *
277
+ * @param item - The questionnaire item being answered.
278
+ * @param previousAnswers - The item's answers before the change.
279
+ * @param newAnswers - The answers requested by the change.
280
+ * @returns The reconciled answers.
281
+ */
282
+ export declare function applyOptionExclusive(item: QuestionnaireItem, previousAnswers: QuestionnaireResponseItemAnswer[] | undefined, newAnswers: QuestionnaireResponseItemAnswer[]): QuestionnaireResponseItemAnswer[];
283
+
264
284
  export declare function AppShell(props: AppShellProps): JSX.Element;
265
285
 
266
286
  export declare interface AppShellAnnouncement {
@@ -288,6 +308,7 @@ export declare interface AppShellProps {
288
308
  readonly layoutVersion?: 'v1' | 'v2';
289
309
  readonly showLayoutVersionToggle?: boolean;
290
310
  readonly spotlightPatientsOnly?: boolean;
311
+ readonly spotlightActions?: SpotlightLinkAction[];
291
312
  }
292
313
 
293
314
  export declare function AsyncAutocomplete<T>(props: AsyncAutocompleteProps<T>): JSX.Element;
@@ -1119,8 +1140,8 @@ export declare interface LinkTabsProps extends Omit<TabsProps, 'value' | 'onChan
1119
1140
  /**
1120
1141
  * ListWithDetailPane is a generic, presentational master-detail shell: a left sidebar
1121
1142
  * with optional pill tabs, header actions, a scrollable list, and pagination, plus a
1122
- * detail area for the selected item. It does no data fetching or routing it renders
1123
- * what it is given and emits `onTabChange` / `onPageChange` callbacks.
1143
+ * detail area for the selected item. It does no data fetching it renders what it is
1144
+ * given, navigates via links, and emits `onPageChange` / `onSelectFirst` callbacks.
1124
1145
  * @param props - The ListWithDetailPane React props.
1125
1146
  * @returns The ListWithDetailPane React node.
1126
1147
  */
@@ -1147,37 +1168,47 @@ export declare type ListWithDetailPaneProps<T extends {
1147
1168
  id?: string;
1148
1169
  } = Resource> = ListWithDetailPanePropsBase<T> & ListWithDetailPaneHeaderProps;
1149
1170
 
1150
- /** Props shared by every ListWithDetailPane, independent of the header style. */
1171
+ /**
1172
+ * Props shared by every ListWithDetailPane, independent of the header style.
1173
+ * @param items - The current page of items to render in the list.
1174
+ * @param loading - When true, the list area shows the skeleton instead of items.
1175
+ * @param selectedKey - Id of the highlighted row.
1176
+ * @param renderItem - Renders one row of the list sidebar.
1177
+ * @param emptyList - Shown when the list is empty. Default: dimmed "No items found".
1178
+ * @param skeleton - Shown while loading. Default: built-in skeleton rows.
1179
+ * @param listWidth - Sidebar width in pixels. Default 350.
1180
+ * @param headerActions - Right-aligned slot in the sidebar header row: action buttons, filter popovers.
1181
+ * @param selected - The resolved selected item, or undefined when nothing is selected.
1182
+ * @param renderDetail - Renders the detail pane for the selected item.
1183
+ * @param emptyDetail - Shown when nothing is selected. Default: dimmed prompt.
1184
+ * @param refresh - Passed through to the detail render context.
1185
+ * @param onSelectFirst - Auto-select escape hatch. Fired with the first item when the list has finished loading
1186
+ * (`loading` false) with items while nothing is selected (`selectedKey` undefined). The consumer decides how to
1187
+ * navigate (typically with history replace). Pass an id-driven `selectedKey` (e.g. the URL route param) so a
1188
+ * selection that is still resolving does not read as "nothing selected", and keep `loading` true from the render a
1189
+ * new search starts on, so this never fires against a stale list.
1190
+ * @param page - Current 1-based page. Pagination is hidden unless this, `pageCount`, and `onPageChange` are set.
1191
+ * @param pageCount - Total number of pages. Pagination is hidden when this is less than or equal to 1.
1192
+ * @param onPageChange - Fired by the built-in pagination with the new 1-based page.
1193
+ */
1151
1194
  export declare interface ListWithDetailPanePropsBase<T extends {
1152
1195
  id?: string;
1153
1196
  } = Resource> {
1154
- /** The current page of items to render in the list. */
1155
1197
  readonly items: T[];
1156
- /** When true, the list area shows the skeleton instead of items. */
1157
1198
  readonly loading: boolean;
1158
- /** Id of the highlighted row. */
1159
1199
  readonly selectedKey?: string;
1160
1200
  readonly renderItem: (item: T, ctx: ListWithDetailPaneItemContext<T>) => ReactNode;
1161
- /** Shown when the list is empty. Default: dimmed "No items found". */
1162
1201
  readonly emptyList?: ReactNode;
1163
- /** Shown while loading. Default: built-in skeleton rows. */
1164
1202
  readonly skeleton?: ReactNode;
1165
- /** Sidebar width in pixels. Default 350. */
1166
1203
  readonly listWidth?: number;
1167
- /** Right-aligned slot in the sidebar header row: action buttons, filter popovers. */
1168
1204
  readonly headerActions?: ReactNode;
1169
- /** The resolved selected item, or undefined when nothing is selected. */
1170
1205
  readonly selected: T | undefined;
1171
1206
  readonly renderDetail: (selected: T, ctx: ListWithDetailPaneDetailContext) => ReactNode;
1172
- /** Shown when nothing is selected. Default: dimmed prompt. */
1173
1207
  readonly emptyDetail?: ReactNode;
1174
- /** Passed through to the detail render context. */
1175
1208
  readonly refresh: () => Promise<void>;
1176
- /** Current 1-based page. Pagination is hidden unless this, `pageCount`, and `onPageChange` are set. */
1209
+ readonly onSelectFirst?: (item: T) => void;
1177
1210
  readonly page?: number;
1178
- /** Total number of pages. Pagination is hidden when this is less than or equal to 1. */
1179
1211
  readonly pageCount?: number;
1180
- /** Fired by the built-in pagination with the new 1-based page. */
1181
1212
  readonly onPageChange?: (page: number) => void;
1182
1213
  }
1183
1214
 
@@ -1189,24 +1220,25 @@ export declare interface ListWithDetailPaneTab {
1189
1220
 
1190
1221
  /**
1191
1222
  * Pill-tab header. Declares `headerText` as `never` so tabs can't be mixed with a title.
1223
+ * Each tab renders as a link to its `uri`, so tab switching needs no change callback.
1224
+ * @param tabs - Sidebar header tabs. Selecting a tab navigates to its URI.
1225
+ * @param activeTab - Controlled active tab value; consumers derive it from the URL.
1192
1226
  */
1193
1227
  export declare interface ListWithDetailPaneTabsHeaderProps {
1194
1228
  readonly tabs: ListWithDetailPaneTab[];
1195
1229
  readonly activeTab?: string;
1196
- readonly onTabChange?: (value: string) => void;
1197
1230
  readonly headerText?: never;
1198
1231
  }
1199
1232
 
1200
1233
  /**
1201
1234
  * Plain-text header (or no header at all). Declares the tab fields as `never` so a title
1202
1235
  * can't be mixed with tabs.
1236
+ * @param headerText - Plain title shown at the left of the header.
1203
1237
  */
1204
1238
  export declare interface ListWithDetailPaneTextHeaderProps {
1205
- /** Plain title shown at the left of the header. */
1206
1239
  readonly headerText?: ReactNode;
1207
1240
  readonly tabs?: never;
1208
1241
  readonly activeTab?: never;
1209
- readonly onTabChange?: never;
1210
1242
  }
1211
1243
 
1212
1244
  export declare function Loading(): JSX.Element;
@@ -1352,6 +1384,39 @@ export declare interface MfaVerificationFormProps {
1352
1384
  readonly buttonText?: string;
1353
1385
  }
1354
1386
 
1387
+ /**
1388
+ * A modal with the standard Medplum chrome: a bold title above a border, one scrolling body, and
1389
+ * action buttons pinned to the bottom edge. Owns the layout so call sites pass content and actions
1390
+ * rather than assembling a shell out of `Stack`, `Divider` and `styles` overrides.
1391
+ *
1392
+ * All other Mantine `Modal` props pass through. `scrollAreaComponent` does not, because it inserts
1393
+ * an element between the content and the body that breaks the flex chain the pinned footer needs.
1394
+ * @param props - The Modal React props.
1395
+ * @returns The Modal React node.
1396
+ */
1397
+ export declare function Modal(props: ModalProps): JSX.Element;
1398
+
1399
+ /**
1400
+ * Props for {@link Modal}.
1401
+ * @property children - Modal body, rendered in the scrolling region between the header and the
1402
+ * footer.
1403
+ * @property actions - Buttons pinned to the bottom edge of the modal above a full-bleed border.
1404
+ * They stretch to the full width with the primary action first; wrap them in a
1405
+ * `Group justify="flex-end"` for a right-aligned row instead. Omit for modals that only display
1406
+ * content - the footer and its border are then not rendered.
1407
+ * @property onSubmit - Wraps the body and the footer in a {@link Form}, so a `SubmitButton` in
1408
+ * `actions` submits the named inputs in `children`. A `SubmitButton` without this has no form to
1409
+ * submit and does nothing.
1410
+ * @property bodyHeight - Fixed body height as a CSS length, e.g. `'60vh'`, for modals whose content
1411
+ * is a fixed layout rather than a form that should size to its fields. Omit to size to the content.
1412
+ */
1413
+ export declare interface ModalProps extends Omit<ModalProps_2, 'children' | 'onSubmit' | 'scrollAreaComponent'> {
1414
+ readonly children: ReactNode;
1415
+ readonly actions?: ReactNode;
1416
+ readonly onSubmit?: (formData: Record<string, string>) => Promise<void> | void;
1417
+ readonly bodyHeight?: string;
1418
+ }
1419
+
1355
1420
  export declare function MoneyDisplay(props: MoneyDisplayProps): JSX.Element | null;
1356
1421
 
1357
1422
  export declare interface MoneyDisplayProps {
@@ -1422,6 +1487,7 @@ export declare interface NavbarProps {
1422
1487
  readonly closeNavbar: () => void;
1423
1488
  readonly spotlightEnabled?: boolean;
1424
1489
  readonly patientsOnly?: boolean;
1490
+ readonly spotlightActions?: SpotlightLinkAction[];
1425
1491
  readonly userMenuEnabled?: boolean;
1426
1492
  readonly displayAddBookmark?: boolean;
1427
1493
  readonly resourceTypeSearchDisabled?: boolean;
@@ -1695,6 +1761,8 @@ export declare const QUESTIONNAIRE_HIDDEN_URL = "http://hl7.org/fhir/StructureDe
1695
1761
 
1696
1762
  export declare const QUESTIONNAIRE_ITEM_CONTROL_URL = "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl";
1697
1763
 
1764
+ export declare const QUESTIONNAIRE_OPTION_EXCLUSIVE_URL = "http://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive";
1765
+
1698
1766
  export declare const QUESTIONNAIRE_REFERENCE_FILTER_URL = "http://hl7.org/fhir/StructureDefinition/questionnaire-referenceFilter";
1699
1767
 
1700
1768
  export declare const QUESTIONNAIRE_REFERENCE_RESOURCE_URL = "http://hl7.org/fhir/StructureDefinition/questionnaire-referenceResource";
@@ -2024,61 +2092,54 @@ export declare interface ResourceBoardLoadResult<T extends Resource = Resource>
2024
2092
  readonly total?: number;
2025
2093
  }
2026
2094
 
2095
+ /**
2096
+ * Props for the ResourceBoard component.
2097
+ * @param search - The search definition (resourceType + filters + count/offset), like SearchControl. Default
2098
+ * fetching searches with `_total=accurate` and `cache: 'no-cache'`. Deep-equality memoized internally, so parents
2099
+ * may pass object literals.
2100
+ * @param selectedId - Selected resource id, typically driven by the URL route param.
2101
+ * @param loadItems - Custom fetcher replacing the default search (e.g. GraphQL batching, client-side filtering).
2102
+ * Re-runs whenever its identity or the search changes — wrap in useCallback.
2103
+ * @param resolveSelected - Selected-resource resolution. Default: find in items by id, else
2104
+ * `medplum.readResource(search.resourceType, id)`.
2105
+ * @param reloadKey - Manual refresh trigger: change this value (e.g. a counter) to re-run the load without changing
2106
+ * the search. Reloads in place — no skeleton.
2107
+ * @param headerText - Plain title shown at the left of the header when no `tabs` are provided.
2108
+ * @param tabs - Sidebar header tabs. Selecting a tab navigates to its URI.
2109
+ * @param activeTab - Controlled active tab value; consumers derive it from the URL.
2110
+ * @param headerActions - Right-aligned slot in the sidebar header row: action buttons, filter popovers.
2111
+ * @param renderItem - Renders one row of the list sidebar.
2112
+ * @param emptyList - Shown when the list loads empty. Default: dimmed "No items found".
2113
+ * @param skeleton - Shown while the initial or search-change load is in flight. Default: built-in skeleton rows.
2114
+ * @param listWidth - Sidebar width in pixels. Default 350.
2115
+ * @param renderDetail - Renders the detail pane for the resolved selection.
2116
+ * @param emptyDetail - Shown when nothing is selected or the selection cannot be resolved.
2117
+ * @param onChange - Fired by the built-in pagination with the updated offset.
2118
+ * @param onSelectFirst - Auto-select escape hatch. Fired with the first item when a load for the current search
2119
+ * settles with items while nothing is selected (owned by ListWithDetailPane). The consumer decides how to navigate
2120
+ * (e.g. with history replace).
2121
+ * @param onLoad - Fired after every successful load.
2122
+ * @param onError - List-load and selection-resolution errors. Default: console.error.
2123
+ */
2027
2124
  export declare interface ResourceBoardProps<T extends Resource = Resource> {
2028
- /**
2029
- * The search definition (resourceType + filters + count/offset), like SearchControl.
2030
- * Default fetching searches with `_total=accurate` and `cache: 'no-cache'`.
2031
- * Deep-equality memoized internally, so parents may pass object literals.
2032
- */
2033
2125
  readonly search: SearchRequest;
2034
- /** Selected resource id, typically driven by the URL route param. */
2035
2126
  readonly selectedId?: string;
2036
- /**
2037
- * Custom fetcher replacing the default search (e.g. GraphQL batching, client-side
2038
- * filtering). Re-runs whenever its identity or the search changes — wrap in useCallback.
2039
- */
2040
2127
  readonly loadItems?: (search: SearchRequest, medplum: MedplumClient) => Promise<ResourceBoardLoadResult<T>>;
2041
- /**
2042
- * Selected-resource resolution. Default: find in items by id, else
2043
- * `medplum.readResource(search.resourceType, id)`.
2044
- */
2045
2128
  readonly resolveSelected?: (id: string, items: WithId<T>[], medplum: MedplumClient) => Promise<WithId<T> | undefined>;
2046
- /**
2047
- * Manual refresh trigger: change this value (e.g. a counter) to re-run the load
2048
- * without changing the search. Reloads in place — no skeleton.
2049
- */
2050
2129
  readonly reloadKey?: unknown;
2051
- /** Plain title shown at the left of the header when no `tabs` are provided. */
2052
2130
  readonly headerText?: ReactNode;
2053
- /** Sidebar header tabs. Selecting a tab navigates to its URI. */
2054
2131
  readonly tabs?: ResourceBoardTab[];
2055
- /** Controlled active tab value; consumers derive it from the URL. */
2056
2132
  readonly activeTab?: string;
2057
- /** Right-aligned slot in the sidebar header row: action buttons, filter popovers. */
2058
2133
  readonly headerActions?: ReactNode;
2059
2134
  readonly renderItem: (item: WithId<T>, ctx: ResourceBoardItemContext<T>) => ReactNode;
2060
- /** Shown when the list loads empty. Default: dimmed "No items found". */
2061
2135
  readonly emptyList?: ReactNode;
2062
- /** Shown while the initial or search-change load is in flight. Default: built-in skeleton rows. */
2063
2136
  readonly skeleton?: ReactNode;
2064
- /** Sidebar width in pixels. Default 350. */
2065
2137
  readonly listWidth?: number;
2066
2138
  readonly renderDetail: (selected: WithId<T>, ctx: ResourceBoardDetailContext) => ReactNode;
2067
- /** Shown when nothing is selected or the selection cannot be resolved. */
2068
2139
  readonly emptyDetail?: ReactNode;
2069
- /**
2070
- * Fired by the built-in pagination with the updated offset.
2071
- */
2072
2140
  readonly onChange?: (search: SearchRequest) => void;
2073
- /**
2074
- * Auto-select escape hatch. Called with the first item when a load for the
2075
- * current search completes with items while `selectedId` is undefined.
2076
- * The consumer decides how to navigate (e.g. with history replace).
2077
- */
2078
2141
  readonly onSelectFirst?: (item: WithId<T>) => void;
2079
- /** Fired after every successful load. */
2080
2142
  readonly onLoad?: (items: WithId<T>[], total: number | undefined) => void;
2081
- /** List-load and selection-resolution errors. Default: console.error. */
2082
2143
  readonly onError?: (error: unknown) => void;
2083
2144
  }
2084
2145
 
@@ -2505,10 +2566,21 @@ export declare const SmokingStatusSection: PatientSummarySectionConfig;
2505
2566
  */
2506
2567
  export declare function sortByDateAndPriority(resources: Resource[], timelineResource?: Resource): void;
2507
2568
 
2508
- export declare function Spotlight({ patientsOnly }: SpotlightProps): JSX.Element;
2569
+ export declare function Spotlight({ patientsOnly, staticActions }: SpotlightProps): JSX.Element;
2570
+
2571
+ /**
2572
+ * A Spotlight entry. Setting `href` renders it as an anchor rather than a button, so the browser's
2573
+ * "Open link in new tab" context menu and Cmd/Ctrl+click both work. A plain click is handed to the
2574
+ * SPA router instead of triggering a full page load; pass `onClick` to do something other than
2575
+ * navigate to `href`.
2576
+ */
2577
+ export declare interface SpotlightLinkAction extends SpotlightActionData {
2578
+ readonly href?: string;
2579
+ }
2509
2580
 
2510
2581
  export declare interface SpotlightProps {
2511
2582
  readonly patientsOnly?: boolean;
2583
+ readonly staticActions?: SpotlightLinkAction[];
2512
2584
  }
2513
2585
 
2514
2586
  export declare function StatusBadge(props: StatusBadgeProps): JSX.Element;
@@ -2592,10 +2664,14 @@ export declare function ThreadInbox(props: ThreadInboxProps): JSX.Element;
2592
2664
  * @param showPatientSummary - Whether to show the patient summary.
2593
2665
  * @param sections - Optional sections configuration for the patient summary.
2594
2666
  * @param onNew - A function to handle a new thread.
2667
+ * @param onSelectFirst - Fired with the first thread when the list loads with nothing selected; use it to navigate to that thread (with replace) so the inbox auto-selects it.
2595
2668
  * @param getThreadUri - A function to build thread URIs.
2596
2669
  * @param onChange - A function to handle search changes.
2597
2670
  * @param inProgressUri - The URI for in-progress threads.
2598
2671
  * @param completedUri - The URI for completed threads.
2672
+ * @param newTopicOpened - Controlled open state for the new topic dialog. When provided, use `onNewTopicOpen` and `onNewTopicClose` to update it.
2673
+ * @param onNewTopicOpen - Called when the user clicks the new message button. Required when `newTopicOpened` is provided.
2674
+ * @param onNewTopicClose - Called when the new topic dialog is closed. Required when `newTopicOpened` is provided.
2599
2675
  */
2600
2676
  export declare interface ThreadInboxProps {
2601
2677
  readonly query: string;
@@ -2604,6 +2680,7 @@ export declare interface ThreadInboxProps {
2604
2680
  readonly showPatientSummary?: boolean;
2605
2681
  readonly sections?: PatientSummarySectionConfig[];
2606
2682
  readonly onNew: (message: Communication) => void;
2683
+ readonly onSelectFirst?: (thread: Communication) => void;
2607
2684
  readonly getThreadUri: (topic: Communication) => string;
2608
2685
  readonly onChange: (search: SearchRequest) => void;
2609
2686
  readonly inProgressUri: string;
@@ -2611,6 +2688,9 @@ export declare interface ThreadInboxProps {
2611
2688
  readonly uploadEnabled?: boolean;
2612
2689
  readonly onViewInDocuments?: (reference: Reference<DocumentReference>) => void;
2613
2690
  readonly allowPatientSelection?: boolean;
2691
+ readonly newTopicOpened?: boolean;
2692
+ readonly onNewTopicOpen?: () => void;
2693
+ readonly onNewTopicClose?: () => void;
2614
2694
  }
2615
2695
 
2616
2696
  export declare function Timeline(props: TimelineProps): JSX.Element;
@@ -2938,7 +3018,6 @@ export declare interface UseResourceBoardProps<T extends Resource = Resource> {
2938
3018
  * without changing the search. Reloads in place — no skeleton — like `refresh()`.
2939
3019
  */
2940
3020
  readonly reloadKey?: unknown;
2941
- readonly onSelectFirst?: (item: WithId<T>) => void;
2942
3021
  readonly onLoad?: (items: WithId<T>[], total: number | undefined) => void;
2943
3022
  readonly onError?: (error: unknown) => void;
2944
3023
  }