@medplum/react 2.0.20 → 2.0.22

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 (153) hide show
  1. package/dist/cjs/index.cjs +218 -151
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/cjs/index.min.cjs +1 -1
  4. package/dist/esm/AppShell/AppShell.mjs +2 -2
  5. package/dist/esm/AppShell/AppShell.mjs.map +1 -1
  6. package/dist/esm/AppShell/Header.mjs +1 -1
  7. package/dist/esm/AppShell/Header.mjs.map +1 -1
  8. package/dist/esm/AppShell/HeaderSearchInput.mjs +1 -1
  9. package/dist/esm/AppShell/HeaderSearchInput.mjs.map +1 -1
  10. package/dist/esm/AsyncAutocomplete/AsyncAutocomplete.mjs +2 -1
  11. package/dist/esm/AsyncAutocomplete/AsyncAutocomplete.mjs.map +1 -1
  12. package/dist/esm/AttachmentButton/AttachmentButton.mjs +1 -2
  13. package/dist/esm/AttachmentButton/AttachmentButton.mjs.map +1 -1
  14. package/dist/esm/DateTimeInput/DateTimeInput.mjs +1 -0
  15. package/dist/esm/DateTimeInput/DateTimeInput.mjs.map +1 -1
  16. package/dist/esm/FhirPathTable/FhirPathTable.mjs +2 -0
  17. package/dist/esm/FhirPathTable/FhirPathTable.mjs.map +1 -1
  18. package/dist/esm/Form/FormUtils.mjs +2 -1
  19. package/dist/esm/Form/FormUtils.mjs.map +1 -1
  20. package/dist/esm/MedplumProvider/MedplumProvider.mjs +12 -0
  21. package/dist/esm/MedplumProvider/MedplumProvider.mjs.map +1 -1
  22. package/dist/esm/PlanDefinitionBuilder/PlanDefinitionBuilder.mjs +2 -1
  23. package/dist/esm/PlanDefinitionBuilder/PlanDefinitionBuilder.mjs.map +1 -1
  24. package/dist/esm/QuestionnaireBuilder/QuestionnaireBuilder.mjs +3 -2
  25. package/dist/esm/QuestionnaireBuilder/QuestionnaireBuilder.mjs.map +1 -1
  26. package/dist/esm/ReferenceRangeEditor/ReferenceRangeEditor.mjs +13 -5
  27. package/dist/esm/ReferenceRangeEditor/ReferenceRangeEditor.mjs.map +1 -1
  28. package/dist/esm/ResourceName/ResourceName.mjs +0 -5
  29. package/dist/esm/ResourceName/ResourceName.mjs.map +1 -1
  30. package/dist/esm/ResourcePropertyDisplay/ResourcePropertyDisplay.mjs +6 -3
  31. package/dist/esm/ResourcePropertyDisplay/ResourcePropertyDisplay.mjs.map +1 -1
  32. package/dist/esm/ResourceTimeline/ResourceTimeline.mjs +1 -1
  33. package/dist/esm/ResourceTimeline/ResourceTimeline.mjs.map +1 -1
  34. package/dist/esm/SearchControl/SearchControl.mjs +3 -2
  35. package/dist/esm/SearchControl/SearchControl.mjs.map +1 -1
  36. package/dist/esm/SearchControl/SearchUtils.mjs +77 -54
  37. package/dist/esm/SearchControl/SearchUtils.mjs.map +1 -1
  38. package/dist/esm/SearchFieldEditor/SearchFieldEditor.mjs +6 -7
  39. package/dist/esm/SearchFieldEditor/SearchFieldEditor.mjs.map +1 -1
  40. package/dist/esm/Timeline/Timeline.mjs +2 -1
  41. package/dist/esm/Timeline/Timeline.mjs.map +1 -1
  42. package/dist/esm/ValueSetAutocomplete/ValueSetAutocomplete.mjs +14 -5
  43. package/dist/esm/ValueSetAutocomplete/ValueSetAutocomplete.mjs.map +1 -1
  44. package/dist/esm/auth/SignInForm.mjs +10 -4
  45. package/dist/esm/auth/SignInForm.mjs.map +1 -1
  46. package/dist/esm/index.min.mjs +1 -1
  47. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs +1 -1
  48. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/createReactComponent.mjs.map +1 -1
  49. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/defaultAttributes.mjs +1 -1
  50. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/defaultAttributes.mjs.map +1 -1
  51. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconAdjustmentsHorizontal.mjs +1 -1
  52. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconAdjustmentsHorizontal.mjs.map +1 -1
  53. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconAlertCircle.mjs +1 -1
  54. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconAlertCircle.mjs.map +1 -1
  55. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBleach.mjs +1 -1
  56. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBleach.mjs.map +1 -1
  57. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBleachOff.mjs +1 -1
  58. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBleachOff.mjs.map +1 -1
  59. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBoxMultiple.mjs +1 -1
  60. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBoxMultiple.mjs.map +1 -1
  61. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBracketsContain.mjs +1 -1
  62. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBracketsContain.mjs.map +1 -1
  63. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBucket.mjs +1 -1
  64. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBucket.mjs.map +1 -1
  65. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBucketOff.mjs +1 -1
  66. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconBucketOff.mjs.map +1 -1
  67. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCalendar.mjs +1 -1
  68. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCalendar.mjs.map +1 -1
  69. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCheck.mjs +1 -1
  70. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCheck.mjs.map +1 -1
  71. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCheckbox.mjs +1 -1
  72. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCheckbox.mjs.map +1 -1
  73. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconChevronDown.mjs +1 -1
  74. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconChevronDown.mjs.map +1 -1
  75. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCircleMinus.mjs +1 -1
  76. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCircleMinus.mjs.map +1 -1
  77. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCirclePlus.mjs +1 -1
  78. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCirclePlus.mjs.map +1 -1
  79. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCloudUpload.mjs +1 -1
  80. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCloudUpload.mjs.map +1 -1
  81. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconColumns.mjs +1 -1
  82. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconColumns.mjs.map +1 -1
  83. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCurrencyDollar.mjs +1 -1
  84. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconCurrencyDollar.mjs.map +1 -1
  85. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconDots.mjs +1 -1
  86. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconDots.mjs.map +1 -1
  87. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconEdit.mjs +1 -1
  88. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconEdit.mjs.map +1 -1
  89. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconEqual.mjs +1 -1
  90. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconEqual.mjs.map +1 -1
  91. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconEqualNot.mjs +1 -1
  92. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconEqualNot.mjs.map +1 -1
  93. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconFileAlert.mjs +1 -1
  94. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconFileAlert.mjs.map +1 -1
  95. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconFilePlus.mjs +1 -1
  96. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconFilePlus.mjs.map +1 -1
  97. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconFilter.mjs +1 -1
  98. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconFilter.mjs.map +1 -1
  99. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconListDetails.mjs +1 -1
  100. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconListDetails.mjs.map +1 -1
  101. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconLogout.mjs +1 -1
  102. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconLogout.mjs.map +1 -1
  103. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconMathGreater.mjs +1 -1
  104. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconMathGreater.mjs.map +1 -1
  105. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconMathLower.mjs +1 -1
  106. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconMathLower.mjs.map +1 -1
  107. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconMessage.mjs +1 -1
  108. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconMessage.mjs.map +1 -1
  109. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconPin.mjs +1 -1
  110. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconPin.mjs.map +1 -1
  111. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconPinnedOff.mjs +1 -1
  112. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconPinnedOff.mjs.map +1 -1
  113. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconPlus.mjs +1 -1
  114. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconPlus.mjs.map +1 -1
  115. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSearch.mjs +1 -1
  116. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSearch.mjs.map +1 -1
  117. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSettings.mjs +1 -1
  118. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSettings.mjs.map +1 -1
  119. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSortAscending.mjs +1 -1
  120. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSortAscending.mjs.map +1 -1
  121. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSortDescending.mjs +1 -1
  122. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSortDescending.mjs.map +1 -1
  123. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSquare.mjs +1 -1
  124. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSquare.mjs.map +1 -1
  125. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSwitchHorizontal.mjs +1 -1
  126. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconSwitchHorizontal.mjs.map +1 -1
  127. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconTableExport.mjs +1 -1
  128. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconTableExport.mjs.map +1 -1
  129. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconTrash.mjs +1 -1
  130. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconTrash.mjs.map +1 -1
  131. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconX.mjs +1 -1
  132. package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconX.mjs.map +1 -1
  133. package/dist/esm/useResource/useResource.mjs +1 -0
  134. package/dist/esm/useResource/useResource.mjs.map +1 -1
  135. package/dist/esm/utils/date.mjs +9 -6
  136. package/dist/esm/utils/date.mjs.map +1 -1
  137. package/dist/esm/utils/script.mjs +1 -0
  138. package/dist/esm/utils/script.mjs.map +1 -1
  139. package/dist/types/AppShell/Header.d.ts +2 -0
  140. package/dist/types/AppShell/HeaderSearchInput.d.ts +1 -0
  141. package/dist/types/FhirPathTable/FhirPathTable.d.ts +2 -0
  142. package/dist/types/Form/FormUtils.d.ts +1 -0
  143. package/dist/types/MedplumProvider/MedplumProvider.d.ts +4 -0
  144. package/dist/types/ResourceName/ResourceName.d.ts +0 -5
  145. package/dist/types/ResourcePropertyDisplay/ResourcePropertyDisplay.d.ts +4 -2
  146. package/dist/types/SearchControl/SearchControl.d.ts +2 -0
  147. package/dist/types/SearchControl/SearchUtils.d.ts +64 -44
  148. package/dist/types/Timeline/Timeline.d.ts +1 -0
  149. package/dist/types/ValueSetAutocomplete/ValueSetAutocomplete.d.ts +3 -0
  150. package/dist/types/auth/SignInForm.d.ts +2 -0
  151. package/dist/types/useResource/useResource.d.ts +1 -0
  152. package/dist/types/utils/script.d.ts +1 -0
  153. package/package.json +12 -12
@@ -28,8 +28,8 @@ function AppShell(props) {
28
28
  main: {
29
29
  background: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.colors.gray[0],
30
30
  },
31
- }, padding: 0, fixed: true, header: profile && React.createElement(Header, { logo: props.logo, version: props.version, navbarToggle: toggleNavbar }), navbar: profile && navbarOpen ? (React.createElement(Navbar, { pathname: props.pathname, searchParams: props.searchParams, menus: props.menus, closeNavbar: closeNavbar, displayAddBookmark: props.displayAddBookmark })) : undefined },
32
- React.createElement(ErrorBoundary, null,
31
+ }, padding: 0, fixed: true, header: profile && (React.createElement(Header, { pathname: props.pathname, searchParams: props.searchParams, logo: props.logo, version: props.version, navbarToggle: toggleNavbar })), navbar: profile && navbarOpen ? (React.createElement(Navbar, { pathname: props.pathname, searchParams: props.searchParams, menus: props.menus, closeNavbar: closeNavbar, displayAddBookmark: props.displayAddBookmark })) : undefined },
32
+ React.createElement(ErrorBoundary, { key: `${props.pathname}?${props.searchParams?.toString()}` },
33
33
  React.createElement(Suspense, { fallback: React.createElement(Loading, null) }, props.children))));
34
34
  }
35
35
 
@@ -1 +1 @@
1
- {"version":3,"file":"AppShell.mjs","sources":["../../../src/AppShell/AppShell.tsx"],"sourcesContent":["import { AppShell as MantineAppShell, useMantineTheme } from '@mantine/core';\nimport React, { Suspense, useState } from 'react';\nimport { ErrorBoundary } from '../ErrorBoundary/ErrorBoundary';\nimport { Loading } from '../Loading/Loading';\nimport { useMedplum, useMedplumProfile } from '../MedplumProvider/MedplumProvider';\nimport { Header } from './Header';\nimport { Navbar, NavbarMenu } from './Navbar';\n\nexport interface AppShellProps {\n logo: React.ReactNode;\n pathname?: string;\n searchParams?: URLSearchParams;\n version?: string;\n menus?: NavbarMenu[];\n children: React.ReactNode;\n displayAddBookmark?: boolean;\n}\n\nexport function AppShell(props: AppShellProps): JSX.Element {\n const theme = useMantineTheme();\n const [navbarOpen, setNavbarOpen] = useState(localStorage['navbarOpen'] === 'true');\n const medplum = useMedplum();\n const profile = useMedplumProfile();\n\n function setNavbarOpenWrapper(open: boolean): void {\n localStorage['navbarOpen'] = open.toString();\n setNavbarOpen(open);\n }\n\n function closeNavbar(): void {\n setNavbarOpenWrapper(false);\n }\n\n function toggleNavbar(): void {\n setNavbarOpenWrapper(!navbarOpen);\n }\n\n if (medplum.isLoading()) {\n return <Loading />;\n }\n\n return (\n <MantineAppShell\n styles={{\n main: {\n background: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.colors.gray[0],\n },\n }}\n padding={0}\n fixed={true}\n header={profile && <Header logo={props.logo} version={props.version} navbarToggle={toggleNavbar} />}\n navbar={\n profile && navbarOpen ? (\n <Navbar\n pathname={props.pathname}\n searchParams={props.searchParams}\n menus={props.menus}\n closeNavbar={closeNavbar}\n displayAddBookmark={props.displayAddBookmark}\n />\n ) : undefined\n }\n >\n <ErrorBoundary>\n <Suspense fallback={<Loading />}>{props.children}</Suspense>\n </ErrorBoundary>\n </MantineAppShell>\n );\n}\n"],"names":["MantineAppShell"],"mappings":";;;;;;;;AAkBM,SAAU,QAAQ,CAAC,KAAoB,EAAA;AAC3C,IAAA,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;AAChC,IAAA,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,MAAM,CAAC,CAAC;AACpF,IAAA,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;AAC7B,IAAA,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IAEpC,SAAS,oBAAoB,CAAC,IAAa,EAAA;QACzC,YAAY,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC7C,aAAa,CAAC,IAAI,CAAC,CAAC;KACrB;AAED,IAAA,SAAS,WAAW,GAAA;QAClB,oBAAoB,CAAC,KAAK,CAAC,CAAC;KAC7B;AAED,IAAA,SAAS,YAAY,GAAA;AACnB,QAAA,oBAAoB,CAAC,CAAC,UAAU,CAAC,CAAC;KACnC;AAED,IAAA,IAAI,OAAO,CAAC,SAAS,EAAE,EAAE;QACvB,OAAO,KAAA,CAAA,aAAA,CAAC,OAAO,EAAA,IAAA,CAAG,CAAC;AACpB,KAAA;AAED,IAAA,QACE,KAAA,CAAA,aAAA,CAACA,UAAe,EAAA,EACd,MAAM,EAAE;AACN,YAAA,IAAI,EAAE;gBACJ,UAAU,EAAE,KAAK,CAAC,WAAW,KAAK,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;AACvF,aAAA;AACF,SAAA,EACD,OAAO,EAAE,CAAC,EACV,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,OAAO,IAAI,KAAA,CAAA,aAAA,CAAC,MAAM,EAAA,EAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,GAAI,EACnG,MAAM,EACJ,OAAO,IAAI,UAAU,IACnB,KAAC,CAAA,aAAA,CAAA,MAAM,EACL,EAAA,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,YAAY,EAAE,KAAK,CAAC,YAAY,EAChC,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,WAAW,EAAE,WAAW,EACxB,kBAAkB,EAAE,KAAK,CAAC,kBAAkB,EAC5C,CAAA,IACA,SAAS,EAAA;AAGf,QAAA,KAAA,CAAA,aAAA,CAAC,aAAa,EAAA,IAAA;AACZ,YAAA,KAAA,CAAA,aAAA,CAAC,QAAQ,EAAA,EAAC,QAAQ,EAAE,oBAAC,OAAO,EAAA,IAAA,CAAG,EAAG,EAAA,KAAK,CAAC,QAAQ,CAAY,CAC9C,CACA,EAClB;AACJ;;;;"}
1
+ {"version":3,"file":"AppShell.mjs","sources":["../../../src/AppShell/AppShell.tsx"],"sourcesContent":["import { AppShell as MantineAppShell, useMantineTheme } from '@mantine/core';\nimport React, { Suspense, useState } from 'react';\nimport { ErrorBoundary } from '../ErrorBoundary/ErrorBoundary';\nimport { Loading } from '../Loading/Loading';\nimport { useMedplum, useMedplumProfile } from '../MedplumProvider/MedplumProvider';\nimport { Header } from './Header';\nimport { Navbar, NavbarMenu } from './Navbar';\n\nexport interface AppShellProps {\n logo: React.ReactNode;\n pathname?: string;\n searchParams?: URLSearchParams;\n version?: string;\n menus?: NavbarMenu[];\n children: React.ReactNode;\n displayAddBookmark?: boolean;\n}\n\nexport function AppShell(props: AppShellProps): JSX.Element {\n const theme = useMantineTheme();\n const [navbarOpen, setNavbarOpen] = useState(localStorage['navbarOpen'] === 'true');\n const medplum = useMedplum();\n const profile = useMedplumProfile();\n\n function setNavbarOpenWrapper(open: boolean): void {\n localStorage['navbarOpen'] = open.toString();\n setNavbarOpen(open);\n }\n\n function closeNavbar(): void {\n setNavbarOpenWrapper(false);\n }\n\n function toggleNavbar(): void {\n setNavbarOpenWrapper(!navbarOpen);\n }\n\n if (medplum.isLoading()) {\n return <Loading />;\n }\n\n return (\n <MantineAppShell\n styles={{\n main: {\n background: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.colors.gray[0],\n },\n }}\n padding={0}\n fixed={true}\n header={\n profile && (\n <Header\n pathname={props.pathname}\n searchParams={props.searchParams}\n logo={props.logo}\n version={props.version}\n navbarToggle={toggleNavbar}\n />\n )\n }\n navbar={\n profile && navbarOpen ? (\n <Navbar\n pathname={props.pathname}\n searchParams={props.searchParams}\n menus={props.menus}\n closeNavbar={closeNavbar}\n displayAddBookmark={props.displayAddBookmark}\n />\n ) : undefined\n }\n >\n <ErrorBoundary key={`${props.pathname}?${props.searchParams?.toString()}`}>\n <Suspense fallback={<Loading />}>{props.children}</Suspense>\n </ErrorBoundary>\n </MantineAppShell>\n );\n}\n"],"names":["MantineAppShell"],"mappings":";;;;;;;;AAkBM,SAAU,QAAQ,CAAC,KAAoB,EAAA;AAC3C,IAAA,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;AAChC,IAAA,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,MAAM,CAAC,CAAC;AACpF,IAAA,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;AAC7B,IAAA,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IAEpC,SAAS,oBAAoB,CAAC,IAAa,EAAA;QACzC,YAAY,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC7C,aAAa,CAAC,IAAI,CAAC,CAAC;KACrB;AAED,IAAA,SAAS,WAAW,GAAA;QAClB,oBAAoB,CAAC,KAAK,CAAC,CAAC;KAC7B;AAED,IAAA,SAAS,YAAY,GAAA;AACnB,QAAA,oBAAoB,CAAC,CAAC,UAAU,CAAC,CAAC;KACnC;AAED,IAAA,IAAI,OAAO,CAAC,SAAS,EAAE,EAAE;QACvB,OAAO,KAAA,CAAA,aAAA,CAAC,OAAO,EAAA,IAAA,CAAG,CAAC;AACpB,KAAA;AAED,IAAA,QACE,KAAA,CAAA,aAAA,CAACA,UAAe,EAAA,EACd,MAAM,EAAE;AACN,YAAA,IAAI,EAAE;gBACJ,UAAU,EAAE,KAAK,CAAC,WAAW,KAAK,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;AACvF,aAAA;AACF,SAAA,EACD,OAAO,EAAE,CAAC,EACV,KAAK,EAAE,IAAI,EACX,MAAM,EACJ,OAAO,KACL,KAAC,CAAA,aAAA,CAAA,MAAM,IACL,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,YAAY,EAAE,KAAK,CAAC,YAAY,EAChC,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,OAAO,EAAE,KAAK,CAAC,OAAO,EACtB,YAAY,EAAE,YAAY,EAC1B,CAAA,CACH,EAEH,MAAM,EACJ,OAAO,IAAI,UAAU,IACnB,KAAA,CAAA,aAAA,CAAC,MAAM,EACL,EAAA,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,YAAY,EAAE,KAAK,CAAC,YAAY,EAChC,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,WAAW,EAAE,WAAW,EACxB,kBAAkB,EAAE,KAAK,CAAC,kBAAkB,EAAA,CAC5C,IACA,SAAS,EAAA;AAGf,QAAA,KAAA,CAAA,aAAA,CAAC,aAAa,EAAA,EAAC,GAAG,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAA,CAAA,EAAI,KAAK,CAAC,YAAY,EAAE,QAAQ,EAAE,CAAE,CAAA,EAAA;AACvE,YAAA,KAAA,CAAA,aAAA,CAAC,QAAQ,EAAA,EAAC,QAAQ,EAAE,oBAAC,OAAO,EAAA,IAAA,CAAG,EAAG,EAAA,KAAK,CAAC,QAAQ,CAAY,CAC9C,CACA,EAClB;AACJ;;;;"}
@@ -49,7 +49,7 @@ function Header(props) {
49
49
  React.createElement(Group, { position: "apart" },
50
50
  React.createElement(Group, { spacing: "xs" },
51
51
  React.createElement(UnstyledButton, { className: classes.logoButton, onClick: props.navbarToggle }, props.logo),
52
- React.createElement(HeaderSearchInput, null)),
52
+ React.createElement(HeaderSearchInput, { pathname: props.pathname, searchParams: props.searchParams })),
53
53
  React.createElement(Menu, { width: 260, shadow: "xl", position: "bottom-end", transitionProps: { transition: 'pop-top-right' }, opened: userMenuOpened, onClose: () => setUserMenuOpened(false) },
54
54
  React.createElement(Menu.Target, null,
55
55
  React.createElement(UnstyledButton, { className: cx(classes.user, { [classes.userActive]: userMenuOpened }), onClick: () => setUserMenuOpened((o) => !o) },
@@ -1 +1 @@
1
- {"version":3,"file":"Header.mjs","sources":["../../../src/AppShell/Header.tsx"],"sourcesContent":["import { Avatar, createStyles, Group, Header as MantineHeader, Menu, Stack, Text, UnstyledButton } from '@mantine/core';\nimport { formatHumanName, getReferenceString, ProfileResource } from '@medplum/core';\nimport { HumanName } from '@medplum/fhirtypes';\nimport { IconChevronDown, IconLogout, IconSettings, IconSwitchHorizontal } from '@tabler/icons-react';\nimport React, { useState } from 'react';\nimport { HumanNameDisplay } from '../HumanNameDisplay/HumanNameDisplay';\nimport { useMedplumContext } from '../MedplumProvider/MedplumProvider';\nimport { ResourceAvatar } from '../ResourceAvatar/ResourceAvatar';\nimport { HeaderSearchInput } from './HeaderSearchInput';\n\nconst useStyles = createStyles((theme) => ({\n logoButton: {\n padding: `${theme.spacing.xs} ${theme.spacing.sm}`,\n borderRadius: theme.radius.sm,\n transition: 'background-color 100ms ease',\n\n '&:hover': {\n backgroundColor: theme.fn.lighten(\n theme.fn.variant({ variant: 'filled', color: theme.primaryColor }).background as string,\n 0.8\n ),\n },\n },\n\n user: {\n padding: `${theme.spacing.xs} ${theme.spacing.sm}`,\n borderRadius: theme.radius.sm,\n transition: 'background-color 100ms ease',\n\n '&:hover': {\n backgroundColor: theme.fn.lighten(\n theme.fn.variant({ variant: 'filled', color: theme.primaryColor }).background as string,\n 0.8\n ),\n },\n },\n\n userName: {\n fontWeight: 500,\n lineHeight: 1,\n marginRight: 3,\n\n [theme.fn.smallerThan('xs')]: {\n display: 'none',\n },\n },\n\n userActive: {\n backgroundColor: theme.fn.lighten(\n theme.fn.variant({ variant: 'filled', color: theme.primaryColor }).background as string,\n 0.8\n ),\n },\n}));\n\ninterface HeaderProps {\n logo: React.ReactNode;\n version?: string;\n navbarToggle: () => void;\n}\n\nexport function Header(props: HeaderProps): JSX.Element {\n const context = useMedplumContext();\n const { medplum, profile, navigate } = context;\n const logins = medplum.getLogins();\n const { classes, cx } = useStyles();\n const [userMenuOpened, setUserMenuOpened] = useState(false);\n\n return (\n <MantineHeader height={60} p={8} style={{ zIndex: 101 }}>\n <Group position=\"apart\">\n <Group spacing=\"xs\">\n <UnstyledButton className={classes.logoButton} onClick={props.navbarToggle}>\n {props.logo}\n </UnstyledButton>\n <HeaderSearchInput />\n </Group>\n\n <Menu\n width={260}\n shadow=\"xl\"\n position=\"bottom-end\"\n transitionProps={{ transition: 'pop-top-right' }}\n opened={userMenuOpened}\n onClose={() => setUserMenuOpened(false)}\n >\n <Menu.Target>\n <UnstyledButton\n className={cx(classes.user, { [classes.userActive]: userMenuOpened })}\n onClick={() => setUserMenuOpened((o) => !o)}\n >\n <Group spacing={7}>\n <ResourceAvatar value={profile} radius=\"xl\" size={24} />\n <Text size=\"sm\" className={classes.userName}>\n {formatHumanName(profile?.name?.[0] as HumanName)}\n </Text>\n <IconChevronDown size={12} stroke={1.5} />\n </Group>\n </UnstyledButton>\n </Menu.Target>\n <Menu.Dropdown>\n <Stack align=\"center\" p=\"xl\">\n <ResourceAvatar size=\"xl\" radius={100} value={context.profile} />\n <HumanNameDisplay value={context.profile?.name?.[0] as HumanName} />\n <Text color=\"dimmed\" size=\"xs\">\n {medplum.getActiveLogin()?.project?.display}\n </Text>\n </Stack>\n {logins.length > 1 && <Menu.Divider />}\n {logins.map(\n (login) =>\n login.profile?.reference !== getReferenceString(context.profile as ProfileResource) && (\n <Menu.Item\n key={login.profile?.reference}\n onClick={() => {\n medplum\n .setActiveLogin(login)\n .then(() => window.location.reload())\n .catch(console.log);\n }}\n >\n <Group>\n <Avatar radius=\"xl\" />\n <div style={{ flex: 1 }}>\n <Text size=\"sm\" weight={500}>\n {login.profile?.display}\n </Text>\n <Text color=\"dimmed\" size=\"xs\">\n {login.project?.display}\n </Text>\n </div>\n </Group>\n </Menu.Item>\n )\n )}\n <Menu.Divider />\n <Menu.Item icon={<IconSwitchHorizontal size={14} stroke={1.5} />} onClick={() => navigate('/signin')}>\n Add another account\n </Menu.Item>\n <Menu.Item\n icon={<IconSettings size={14} stroke={1.5} />}\n onClick={() => navigate(`/${getReferenceString(profile as ProfileResource)}`)}\n >\n Account settings\n </Menu.Item>\n <Menu.Item\n icon={<IconLogout size={14} stroke={1.5} />}\n onClick={async () => {\n await medplum.signOut();\n navigate('/signin');\n }}\n >\n Sign out\n </Menu.Item>\n <Text size=\"xs\" color=\"dimmed\" align=\"center\">\n {props.version}\n </Text>\n </Menu.Dropdown>\n </Menu>\n </Group>\n </MantineHeader>\n );\n}\n"],"names":["MantineHeader"],"mappings":"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"}
1
+ {"version":3,"file":"Header.mjs","sources":["../../../src/AppShell/Header.tsx"],"sourcesContent":["import { Avatar, createStyles, Group, Header as MantineHeader, Menu, Stack, Text, UnstyledButton } from '@mantine/core';\nimport { formatHumanName, getReferenceString, ProfileResource } from '@medplum/core';\nimport { HumanName } from '@medplum/fhirtypes';\nimport { IconChevronDown, IconLogout, IconSettings, IconSwitchHorizontal } from '@tabler/icons-react';\nimport React, { useState } from 'react';\nimport { HumanNameDisplay } from '../HumanNameDisplay/HumanNameDisplay';\nimport { useMedplumContext } from '../MedplumProvider/MedplumProvider';\nimport { ResourceAvatar } from '../ResourceAvatar/ResourceAvatar';\nimport { HeaderSearchInput } from './HeaderSearchInput';\n\nconst useStyles = createStyles((theme) => ({\n logoButton: {\n padding: `${theme.spacing.xs} ${theme.spacing.sm}`,\n borderRadius: theme.radius.sm,\n transition: 'background-color 100ms ease',\n\n '&:hover': {\n backgroundColor: theme.fn.lighten(\n theme.fn.variant({ variant: 'filled', color: theme.primaryColor }).background as string,\n 0.8\n ),\n },\n },\n\n user: {\n padding: `${theme.spacing.xs} ${theme.spacing.sm}`,\n borderRadius: theme.radius.sm,\n transition: 'background-color 100ms ease',\n\n '&:hover': {\n backgroundColor: theme.fn.lighten(\n theme.fn.variant({ variant: 'filled', color: theme.primaryColor }).background as string,\n 0.8\n ),\n },\n },\n\n userName: {\n fontWeight: 500,\n lineHeight: 1,\n marginRight: 3,\n\n [theme.fn.smallerThan('xs')]: {\n display: 'none',\n },\n },\n\n userActive: {\n backgroundColor: theme.fn.lighten(\n theme.fn.variant({ variant: 'filled', color: theme.primaryColor }).background as string,\n 0.8\n ),\n },\n}));\n\ninterface HeaderProps {\n pathname?: string;\n searchParams?: URLSearchParams;\n logo: React.ReactNode;\n version?: string;\n navbarToggle: () => void;\n}\n\nexport function Header(props: HeaderProps): JSX.Element {\n const context = useMedplumContext();\n const { medplum, profile, navigate } = context;\n const logins = medplum.getLogins();\n const { classes, cx } = useStyles();\n const [userMenuOpened, setUserMenuOpened] = useState(false);\n\n return (\n <MantineHeader height={60} p={8} style={{ zIndex: 101 }}>\n <Group position=\"apart\">\n <Group spacing=\"xs\">\n <UnstyledButton className={classes.logoButton} onClick={props.navbarToggle}>\n {props.logo}\n </UnstyledButton>\n <HeaderSearchInput pathname={props.pathname} searchParams={props.searchParams} />\n </Group>\n\n <Menu\n width={260}\n shadow=\"xl\"\n position=\"bottom-end\"\n transitionProps={{ transition: 'pop-top-right' }}\n opened={userMenuOpened}\n onClose={() => setUserMenuOpened(false)}\n >\n <Menu.Target>\n <UnstyledButton\n className={cx(classes.user, { [classes.userActive]: userMenuOpened })}\n onClick={() => setUserMenuOpened((o) => !o)}\n >\n <Group spacing={7}>\n <ResourceAvatar value={profile} radius=\"xl\" size={24} />\n <Text size=\"sm\" className={classes.userName}>\n {formatHumanName(profile?.name?.[0] as HumanName)}\n </Text>\n <IconChevronDown size={12} stroke={1.5} />\n </Group>\n </UnstyledButton>\n </Menu.Target>\n <Menu.Dropdown>\n <Stack align=\"center\" p=\"xl\">\n <ResourceAvatar size=\"xl\" radius={100} value={context.profile} />\n <HumanNameDisplay value={context.profile?.name?.[0] as HumanName} />\n <Text color=\"dimmed\" size=\"xs\">\n {medplum.getActiveLogin()?.project?.display}\n </Text>\n </Stack>\n {logins.length > 1 && <Menu.Divider />}\n {logins.map(\n (login) =>\n login.profile?.reference !== getReferenceString(context.profile as ProfileResource) && (\n <Menu.Item\n key={login.profile?.reference}\n onClick={() => {\n medplum\n .setActiveLogin(login)\n .then(() => window.location.reload())\n .catch(console.log);\n }}\n >\n <Group>\n <Avatar radius=\"xl\" />\n <div style={{ flex: 1 }}>\n <Text size=\"sm\" weight={500}>\n {login.profile?.display}\n </Text>\n <Text color=\"dimmed\" size=\"xs\">\n {login.project?.display}\n </Text>\n </div>\n </Group>\n </Menu.Item>\n )\n )}\n <Menu.Divider />\n <Menu.Item icon={<IconSwitchHorizontal size={14} stroke={1.5} />} onClick={() => navigate('/signin')}>\n Add another account\n </Menu.Item>\n <Menu.Item\n icon={<IconSettings size={14} stroke={1.5} />}\n onClick={() => navigate(`/${getReferenceString(profile as ProfileResource)}`)}\n >\n Account settings\n </Menu.Item>\n <Menu.Item\n icon={<IconLogout size={14} stroke={1.5} />}\n onClick={async () => {\n await medplum.signOut();\n navigate('/signin');\n }}\n >\n Sign out\n </Menu.Item>\n <Text size=\"xs\" color=\"dimmed\" align=\"center\">\n {props.version}\n </Text>\n </Menu.Dropdown>\n </Menu>\n </Group>\n </MantineHeader>\n );\n}\n"],"names":["MantineHeader"],"mappings":"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"}
@@ -52,7 +52,7 @@ function HeaderSearchInput(props) {
52
52
  navigate(`/${getReferenceString(item[0])}`);
53
53
  }
54
54
  }, [navigate]);
55
- return (React.createElement(AsyncAutocomplete, { key: props.pathname, size: "sm", radius: "md", className: classes.searchInput, icon: React.createElement(IconSearch, { size: 16 }), placeholder: "Search", itemComponent: ItemComponent, toKey: toKey, toOption: toOption, onChange: handleSelect, loadOptions: loadData }));
55
+ return (React.createElement(AsyncAutocomplete, { key: `${props.pathname}?${props.searchParams}`, size: "sm", radius: "md", className: classes.searchInput, icon: React.createElement(IconSearch, { size: 16 }), placeholder: "Search", itemComponent: ItemComponent, toKey: toKey, toOption: toOption, onChange: handleSelect, loadOptions: loadData, maxSelectedValues: 0, clearSearchOnChange: true, clearable: false }));
56
56
  }
57
57
  const ItemComponent = forwardRef(({ resource, ...others }, ref) => {
58
58
  let helpText = undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"HeaderSearchInput.mjs","sources":["../../../src/AppShell/HeaderSearchInput.tsx"],"sourcesContent":["import { createStyles, Group, Text } from '@mantine/core';\nimport { formatHumanName, getDisplayString, getReferenceString, isUUID } from '@medplum/core';\nimport { Patient, ServiceRequest } from '@medplum/fhirtypes';\nimport { IconSearch } from '@tabler/icons-react';\nimport React, { forwardRef, useCallback } from 'react';\nimport { AsyncAutocomplete, AsyncAutocompleteOption } from '../AsyncAutocomplete/AsyncAutocomplete';\nimport { useMedplum, useMedplumNavigate } from '../MedplumProvider/MedplumProvider';\nimport { ResourceAvatar } from '../ResourceAvatar/ResourceAvatar';\n\nexport type HeaderSearchTypes = Patient | ServiceRequest;\n\nconst useStyles = createStyles(() => {\n return {\n searchInput: {\n input: {\n width: 220,\n transition: 'width 0.2s',\n },\n 'input:focus': {\n width: 400,\n },\n '@media (max-width: 800px)': {\n input: {\n width: 150,\n },\n 'input:focus': {\n width: 150,\n },\n },\n },\n };\n});\n\ninterface SearchGraphQLResponse {\n readonly data: {\n readonly Patients1: Patient[] | undefined;\n readonly Patients2: Patient[] | undefined;\n readonly ServiceRequestList: ServiceRequest[] | undefined;\n };\n}\n\nfunction toKey(resource: HeaderSearchTypes): string {\n return resource.id as string;\n}\n\nfunction toOption(resource: HeaderSearchTypes): AsyncAutocompleteOption<HeaderSearchTypes> {\n return {\n value: resource.id as string,\n label: getDisplayString(resource),\n resource,\n };\n}\n\nexport interface HeaderSearchInputProps {\n pathname?: string;\n}\n\nexport function HeaderSearchInput(props: HeaderSearchInputProps): JSX.Element {\n const { classes } = useStyles();\n const navigate = useMedplumNavigate();\n const medplum = useMedplum();\n\n const loadData = useCallback(\n async (input: string, signal: AbortSignal): Promise<HeaderSearchTypes[]> => {\n const query = buildGraphQLQuery(input);\n const options = { signal };\n const response = (await medplum.graphql(query, undefined, undefined, options)) as SearchGraphQLResponse;\n return getResourcesFromResponse(response, input);\n },\n [medplum]\n );\n\n const handleSelect = useCallback(\n (item: HeaderSearchTypes[]): void => {\n if (item.length > 0) {\n navigate(`/${getReferenceString(item[0])}`);\n }\n },\n [navigate]\n );\n\n return (\n <AsyncAutocomplete\n key={props.pathname}\n size=\"sm\"\n radius=\"md\"\n className={classes.searchInput}\n icon={<IconSearch size={16} />}\n placeholder=\"Search\"\n itemComponent={ItemComponent}\n toKey={toKey}\n toOption={toOption}\n onChange={handleSelect}\n loadOptions={loadData}\n />\n );\n}\n\nconst ItemComponent = forwardRef<HTMLDivElement, any>(\n ({ resource, ...others }: AsyncAutocompleteOption<HeaderSearchTypes>, ref) => {\n let helpText: string | undefined = undefined;\n\n if (resource.resourceType === 'Patient') {\n helpText = resource.birthDate;\n } else if (resource.resourceType === 'ServiceRequest') {\n helpText = resource.subject?.display;\n }\n\n return (\n <div ref={ref} {...others}>\n <Group noWrap>\n <ResourceAvatar value={resource} />\n <div>\n <Text>{getDisplayString(resource)}</Text>\n <Text size=\"xs\" color=\"dimmed\">\n {helpText}\n </Text>\n </div>\n </Group>\n </div>\n );\n }\n);\n\nfunction buildGraphQLQuery(input: string): string {\n const escaped = JSON.stringify(input);\n if (isUUID(input)) {\n return `{\n Patients1: PatientList(_id: ${escaped}, _count: 1) {\n resourceType\n id\n identifier {\n system\n value\n }\n name {\n given\n family\n }\n birthDate\n }\n ServiceRequestList(_id: ${escaped}, _count: 1) {\n resourceType\n id\n identifier {\n system\n value\n }\n subject {\n display\n }\n }\n }`.replace(/\\s+/g, ' ');\n }\n return `{\n Patients1: PatientList(name: ${escaped}, _count: 5) {\n resourceType\n id\n identifier {\n system\n value\n }\n name {\n given\n family\n }\n birthDate\n }\n Patients2: PatientList(identifier: ${escaped}, _count: 5) {\n resourceType\n id\n identifier {\n system\n value\n }\n name {\n given\n family\n }\n birthDate\n }\n ServiceRequestList(identifier: ${escaped}, _count: 5) {\n resourceType\n id\n identifier {\n system\n value\n }\n subject {\n display\n }\n }\n }`.replace(/\\s+/g, ' ');\n}\n\n/**\n * Returns a de-duped and sorted list of resources from the search response.\n * The search request is actually 3+ separate searches, which can include duplicates.\n * This function combines the results, de-dupes, and sorts by relevance.\n * @param response The response from a search query.\n * @param query The user entered search query.\n * @returns The resources to display in the autocomplete.\n */\nfunction getResourcesFromResponse(response: SearchGraphQLResponse, query: string): HeaderSearchTypes[] {\n const resources = [];\n if (response.data.Patients1) {\n resources.push(...response.data.Patients1);\n }\n if (response.data.Patients2) {\n resources.push(...response.data.Patients2);\n }\n if (response.data.ServiceRequestList) {\n resources.push(...response.data.ServiceRequestList);\n }\n return sortByRelevance(dedupeResources(resources), query).slice(0, 5);\n}\n\n/**\n * Removes duplicate resources from an array by ID.\n * @param resources The array of resources with possible duplicates.\n * @returns The array of resources with no duplicates.\n */\nfunction dedupeResources(resources: HeaderSearchTypes[]): HeaderSearchTypes[] {\n const ids = new Set<string>();\n const result = [];\n\n for (const resource of resources) {\n if (!ids.has(resource.id as string)) {\n ids.add(resource.id as string);\n result.push(resource);\n }\n }\n\n return result;\n}\n\n/**\n * Sorts an array of resources by relevance.\n * @param resources The candidate resources.\n * @param query The user entered search string.\n * @returns The sorted array of resources.\n */\nfunction sortByRelevance(resources: HeaderSearchTypes[], query: string): HeaderSearchTypes[] {\n return resources.sort((a: HeaderSearchTypes, b: HeaderSearchTypes) => {\n return getResourceScore(b, query) - getResourceScore(a, query);\n });\n}\n\n/**\n * Calculates a relevance score of a candidate resource.\n * Higher scores are better.\n * @param resource The candidate resource.\n * @param query The user entered search string.\n * @returns The relevance score of the candidate resource.\n */\nfunction getResourceScore(resource: HeaderSearchTypes, query: string): number {\n let bestScore = 0;\n\n if (resource.identifier) {\n for (const identifier of resource.identifier) {\n bestScore = Math.max(bestScore, getStringScore(identifier.value, query));\n }\n }\n\n if (resource.resourceType === 'Patient' && resource.name) {\n for (const name of resource.name) {\n bestScore = Math.max(bestScore, getStringScore(formatHumanName(name), query));\n }\n }\n\n return bestScore;\n}\n\n/**\n * Calculates a relevance score of a candidate display string.\n * Higher scores are better.\n * @param str The candidate display string.\n * @param query The user entered search string.\n * @returns The relevance score of the candidate string.\n */\nfunction getStringScore(str: string | undefined, query: string): number {\n if (!str) {\n return 0;\n }\n const index = str.toLowerCase().indexOf(query.toLowerCase());\n if (index < 0) {\n return 0;\n }\n return 100 - index;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"HeaderSearchInput.mjs","sources":["../../../src/AppShell/HeaderSearchInput.tsx"],"sourcesContent":["import { createStyles, Group, Text } from '@mantine/core';\nimport { formatHumanName, getDisplayString, getReferenceString, isUUID } from '@medplum/core';\nimport { Patient, ServiceRequest } from '@medplum/fhirtypes';\nimport { IconSearch } from '@tabler/icons-react';\nimport React, { forwardRef, useCallback } from 'react';\nimport { AsyncAutocomplete, AsyncAutocompleteOption } from '../AsyncAutocomplete/AsyncAutocomplete';\nimport { useMedplum, useMedplumNavigate } from '../MedplumProvider/MedplumProvider';\nimport { ResourceAvatar } from '../ResourceAvatar/ResourceAvatar';\n\nexport type HeaderSearchTypes = Patient | ServiceRequest;\n\nconst useStyles = createStyles(() => {\n return {\n searchInput: {\n input: {\n width: 220,\n transition: 'width 0.2s',\n },\n 'input:focus': {\n width: 400,\n },\n '@media (max-width: 800px)': {\n input: {\n width: 150,\n },\n 'input:focus': {\n width: 150,\n },\n },\n },\n };\n});\n\ninterface SearchGraphQLResponse {\n readonly data: {\n readonly Patients1: Patient[] | undefined;\n readonly Patients2: Patient[] | undefined;\n readonly ServiceRequestList: ServiceRequest[] | undefined;\n };\n}\n\nfunction toKey(resource: HeaderSearchTypes): string {\n return resource.id as string;\n}\n\nfunction toOption(resource: HeaderSearchTypes): AsyncAutocompleteOption<HeaderSearchTypes> {\n return {\n value: resource.id as string,\n label: getDisplayString(resource),\n resource,\n };\n}\n\nexport interface HeaderSearchInputProps {\n pathname?: string;\n searchParams?: URLSearchParams;\n}\n\nexport function HeaderSearchInput(props: HeaderSearchInputProps): JSX.Element {\n const { classes } = useStyles();\n const navigate = useMedplumNavigate();\n const medplum = useMedplum();\n\n const loadData = useCallback(\n async (input: string, signal: AbortSignal): Promise<HeaderSearchTypes[]> => {\n const query = buildGraphQLQuery(input);\n const options = { signal };\n const response = (await medplum.graphql(query, undefined, undefined, options)) as SearchGraphQLResponse;\n return getResourcesFromResponse(response, input);\n },\n [medplum]\n );\n\n const handleSelect = useCallback(\n (item: HeaderSearchTypes[]): void => {\n if (item.length > 0) {\n navigate(`/${getReferenceString(item[0])}`);\n }\n },\n [navigate]\n );\n\n return (\n <AsyncAutocomplete\n key={`${props.pathname}?${props.searchParams}`}\n size=\"sm\"\n radius=\"md\"\n className={classes.searchInput}\n icon={<IconSearch size={16} />}\n placeholder=\"Search\"\n itemComponent={ItemComponent}\n toKey={toKey}\n toOption={toOption}\n onChange={handleSelect}\n loadOptions={loadData}\n maxSelectedValues={0}\n clearSearchOnChange\n clearable={false}\n />\n );\n}\n\nconst ItemComponent = forwardRef<HTMLDivElement, any>(\n ({ resource, ...others }: AsyncAutocompleteOption<HeaderSearchTypes>, ref) => {\n let helpText: string | undefined = undefined;\n\n if (resource.resourceType === 'Patient') {\n helpText = resource.birthDate;\n } else if (resource.resourceType === 'ServiceRequest') {\n helpText = resource.subject?.display;\n }\n\n return (\n <div ref={ref} {...others}>\n <Group noWrap>\n <ResourceAvatar value={resource} />\n <div>\n <Text>{getDisplayString(resource)}</Text>\n <Text size=\"xs\" color=\"dimmed\">\n {helpText}\n </Text>\n </div>\n </Group>\n </div>\n );\n }\n);\n\nfunction buildGraphQLQuery(input: string): string {\n const escaped = JSON.stringify(input);\n if (isUUID(input)) {\n return `{\n Patients1: PatientList(_id: ${escaped}, _count: 1) {\n resourceType\n id\n identifier {\n system\n value\n }\n name {\n given\n family\n }\n birthDate\n }\n ServiceRequestList(_id: ${escaped}, _count: 1) {\n resourceType\n id\n identifier {\n system\n value\n }\n subject {\n display\n }\n }\n }`.replace(/\\s+/g, ' ');\n }\n return `{\n Patients1: PatientList(name: ${escaped}, _count: 5) {\n resourceType\n id\n identifier {\n system\n value\n }\n name {\n given\n family\n }\n birthDate\n }\n Patients2: PatientList(identifier: ${escaped}, _count: 5) {\n resourceType\n id\n identifier {\n system\n value\n }\n name {\n given\n family\n }\n birthDate\n }\n ServiceRequestList(identifier: ${escaped}, _count: 5) {\n resourceType\n id\n identifier {\n system\n value\n }\n subject {\n display\n }\n }\n }`.replace(/\\s+/g, ' ');\n}\n\n/**\n * Returns a de-duped and sorted list of resources from the search response.\n * The search request is actually 3+ separate searches, which can include duplicates.\n * This function combines the results, de-dupes, and sorts by relevance.\n * @param response The response from a search query.\n * @param query The user entered search query.\n * @returns The resources to display in the autocomplete.\n */\nfunction getResourcesFromResponse(response: SearchGraphQLResponse, query: string): HeaderSearchTypes[] {\n const resources = [];\n if (response.data.Patients1) {\n resources.push(...response.data.Patients1);\n }\n if (response.data.Patients2) {\n resources.push(...response.data.Patients2);\n }\n if (response.data.ServiceRequestList) {\n resources.push(...response.data.ServiceRequestList);\n }\n return sortByRelevance(dedupeResources(resources), query).slice(0, 5);\n}\n\n/**\n * Removes duplicate resources from an array by ID.\n * @param resources The array of resources with possible duplicates.\n * @returns The array of resources with no duplicates.\n */\nfunction dedupeResources(resources: HeaderSearchTypes[]): HeaderSearchTypes[] {\n const ids = new Set<string>();\n const result = [];\n\n for (const resource of resources) {\n if (!ids.has(resource.id as string)) {\n ids.add(resource.id as string);\n result.push(resource);\n }\n }\n\n return result;\n}\n\n/**\n * Sorts an array of resources by relevance.\n * @param resources The candidate resources.\n * @param query The user entered search string.\n * @returns The sorted array of resources.\n */\nfunction sortByRelevance(resources: HeaderSearchTypes[], query: string): HeaderSearchTypes[] {\n return resources.sort((a: HeaderSearchTypes, b: HeaderSearchTypes) => {\n return getResourceScore(b, query) - getResourceScore(a, query);\n });\n}\n\n/**\n * Calculates a relevance score of a candidate resource.\n * Higher scores are better.\n * @param resource The candidate resource.\n * @param query The user entered search string.\n * @returns The relevance score of the candidate resource.\n */\nfunction getResourceScore(resource: HeaderSearchTypes, query: string): number {\n let bestScore = 0;\n\n if (resource.identifier) {\n for (const identifier of resource.identifier) {\n bestScore = Math.max(bestScore, getStringScore(identifier.value, query));\n }\n }\n\n if (resource.resourceType === 'Patient' && resource.name) {\n for (const name of resource.name) {\n bestScore = Math.max(bestScore, getStringScore(formatHumanName(name), query));\n }\n }\n\n return bestScore;\n}\n\n/**\n * Calculates a relevance score of a candidate display string.\n * Higher scores are better.\n * @param str The candidate display string.\n * @param query The user entered search string.\n * @returns The relevance score of the candidate string.\n */\nfunction getStringScore(str: string | undefined, query: string): number {\n if (!str) {\n return 0;\n }\n const index = str.toLowerCase().indexOf(query.toLowerCase());\n if (index < 0) {\n return 0;\n }\n return 100 - index;\n}\n"],"names":[],"mappings":"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"}
@@ -64,8 +64,9 @@ function AsyncAutocomplete(props) {
64
64
  if (!item && creatable !== false) {
65
65
  item = onCreate(value);
66
66
  }
67
- if (item)
67
+ if (item) {
68
68
  result.push(item);
69
+ }
69
70
  }
70
71
  onChange(result);
71
72
  }, [creatable, onChange, onCreate]);
@@ -1 +1 @@
1
- {"version":3,"file":"AsyncAutocomplete.mjs","sources":["../../../src/AsyncAutocomplete/AsyncAutocomplete.tsx"],"sourcesContent":["import { Loader, MultiSelect, MultiSelectProps, SelectItem } from '@mantine/core';\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\nimport { killEvent } from '../utils/dom';\n\nexport interface AsyncAutocompleteOption<T> extends SelectItem {\n resource: T;\n}\n\nexport interface AsyncAutocompleteProps<T>\n extends Omit<MultiSelectProps, 'data' | 'defaultValue' | 'loadOptions' | 'onChange' | 'onCreate' | 'searchable'> {\n defaultValue?: T | T[];\n toKey: (item: T) => string;\n toOption: (item: T) => AsyncAutocompleteOption<T>;\n loadOptions: (input: string, signal: AbortSignal) => Promise<T[]>;\n onChange: (item: T[]) => void;\n onCreate?: (input: string) => T;\n creatable?: boolean;\n}\n\nexport function AsyncAutocomplete<T>(props: AsyncAutocompleteProps<T>): JSX.Element {\n const { defaultValue, toKey, toOption, loadOptions, onChange, onCreate, creatable, ...rest } = props;\n const defaultItems = toDefaultItems(defaultValue);\n const inputRef = useRef<HTMLInputElement>(null);\n const [lastValue, setLastValue] = useState<string | undefined>(undefined);\n const [timer, setTimer] = useState<number>();\n const [abortController, setAbortController] = useState<AbortController>();\n const [autoSubmit, setAutoSubmit] = useState<boolean>();\n const [options, setOptions] = useState<AsyncAutocompleteOption<T>[]>(defaultItems?.map(toOption));\n\n const lastValueRef = useRef<string>();\n lastValueRef.current = lastValue;\n\n const timerRef = useRef<number>();\n timerRef.current = timer;\n\n const abortControllerRef = useRef<AbortController>();\n abortControllerRef.current = abortController;\n\n const autoSubmitRef = useRef<boolean>();\n autoSubmitRef.current = autoSubmit;\n\n const optionsRef = useRef<AsyncAutocompleteOption<T>[]>();\n optionsRef.current = options;\n\n const handleTimer = useCallback((): void => {\n setTimer(undefined);\n\n const value = inputRef.current?.value?.trim() || '';\n if (value === lastValueRef.current) {\n // Nothing has changed, move on\n return;\n }\n\n setLastValue(value);\n\n const newAbortController = new AbortController();\n setAbortController(newAbortController);\n\n loadOptions(value, newAbortController.signal)\n .then((newValues: T[]) => {\n if (!newAbortController.signal.aborted) {\n setOptions(newValues.map(toOption));\n setAbortController(undefined);\n if (autoSubmitRef.current) {\n if (newValues.length > 0) {\n onChange(newValues.slice(0, 1));\n }\n setAutoSubmit(false);\n }\n }\n })\n .catch(console.log);\n }, [loadOptions, onChange, toOption]);\n\n const handleSearchChange = useCallback((): void => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n setAbortController(undefined);\n }\n\n if (timerRef.current !== undefined) {\n window.clearTimeout(timerRef.current);\n }\n\n const newTimer = window.setTimeout(() => handleTimer(), 100);\n setTimer(newTimer);\n }, [handleTimer]);\n\n const handleChange = useCallback(\n (values: string[]): void => {\n const result: T[] = [];\n for (const value of values) {\n let item = optionsRef.current?.find((option) => option.value === value)?.resource;\n if (!item && creatable !== false) {\n item = (onCreate as (input: string) => T)(value);\n }\n\n if (item) result.push(item);\n }\n onChange(result);\n },\n [creatable, onChange, onCreate]\n );\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent): void => {\n if (e.key === 'Enter') {\n if (!timerRef.current && !abortControllerRef.current) {\n killEvent(e);\n if (optionsRef.current && optionsRef.current.length > 0) {\n setOptions(optionsRef.current.slice(0, 1));\n handleChange([optionsRef.current[0].value]);\n }\n } else {\n // The user pressed enter, but we don't have results yet.\n // We need to wait for the results to come in.\n setAutoSubmit(true);\n }\n }\n },\n [handleChange]\n );\n\n const handleCreate = useCallback(\n (input: string): AsyncAutocompleteOption<T> => {\n const option = toOption((onCreate as (input: string) => T)(input));\n setOptions([...(optionsRef.current as AsyncAutocompleteOption<T>[]), option]);\n return option;\n },\n [onCreate, setOptions, toOption]\n );\n\n const handleFilter = useCallback((_value: string, selected: boolean) => !selected, []);\n\n useEffect(() => {\n return () => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n };\n }, []);\n\n return (\n <MultiSelect\n {...rest}\n ref={inputRef}\n defaultValue={defaultItems.map(toKey)}\n searchable\n onKeyDown={handleKeyDown}\n onSearchChange={handleSearchChange}\n data={options}\n onFocus={handleTimer}\n onChange={handleChange}\n onCreate={handleCreate}\n rightSectionWidth={40}\n rightSection={abortController ? <Loader size={16} /> : null}\n filter={handleFilter}\n creatable\n />\n );\n}\n\nfunction toDefaultItems<T>(defaultValue: T | T[] | undefined): T[] {\n if (!defaultValue) {\n return [];\n }\n if (Array.isArray(defaultValue)) {\n return defaultValue;\n }\n return [defaultValue];\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AsyncAutocomplete.mjs","sources":["../../../src/AsyncAutocomplete/AsyncAutocomplete.tsx"],"sourcesContent":["import { Loader, MultiSelect, MultiSelectProps, SelectItem } from '@mantine/core';\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\nimport { killEvent } from '../utils/dom';\n\nexport interface AsyncAutocompleteOption<T> extends SelectItem {\n resource: T;\n}\n\nexport interface AsyncAutocompleteProps<T>\n extends Omit<MultiSelectProps, 'data' | 'defaultValue' | 'loadOptions' | 'onChange' | 'onCreate' | 'searchable'> {\n defaultValue?: T | T[];\n toKey: (item: T) => string;\n toOption: (item: T) => AsyncAutocompleteOption<T>;\n loadOptions: (input: string, signal: AbortSignal) => Promise<T[]>;\n onChange: (item: T[]) => void;\n onCreate?: (input: string) => T;\n creatable?: boolean;\n}\n\nexport function AsyncAutocomplete<T>(props: AsyncAutocompleteProps<T>): JSX.Element {\n const { defaultValue, toKey, toOption, loadOptions, onChange, onCreate, creatable, ...rest } = props;\n const defaultItems = toDefaultItems(defaultValue);\n const inputRef = useRef<HTMLInputElement>(null);\n const [lastValue, setLastValue] = useState<string | undefined>(undefined);\n const [timer, setTimer] = useState<number>();\n const [abortController, setAbortController] = useState<AbortController>();\n const [autoSubmit, setAutoSubmit] = useState<boolean>();\n const [options, setOptions] = useState<AsyncAutocompleteOption<T>[]>(defaultItems?.map(toOption));\n\n const lastValueRef = useRef<string>();\n lastValueRef.current = lastValue;\n\n const timerRef = useRef<number>();\n timerRef.current = timer;\n\n const abortControllerRef = useRef<AbortController>();\n abortControllerRef.current = abortController;\n\n const autoSubmitRef = useRef<boolean>();\n autoSubmitRef.current = autoSubmit;\n\n const optionsRef = useRef<AsyncAutocompleteOption<T>[]>();\n optionsRef.current = options;\n\n const handleTimer = useCallback((): void => {\n setTimer(undefined);\n\n const value = inputRef.current?.value?.trim() || '';\n if (value === lastValueRef.current) {\n // Nothing has changed, move on\n return;\n }\n\n setLastValue(value);\n\n const newAbortController = new AbortController();\n setAbortController(newAbortController);\n\n loadOptions(value, newAbortController.signal)\n .then((newValues: T[]) => {\n if (!newAbortController.signal.aborted) {\n setOptions(newValues.map(toOption));\n setAbortController(undefined);\n if (autoSubmitRef.current) {\n if (newValues.length > 0) {\n onChange(newValues.slice(0, 1));\n }\n setAutoSubmit(false);\n }\n }\n })\n .catch(console.log);\n }, [loadOptions, onChange, toOption]);\n\n const handleSearchChange = useCallback((): void => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n setAbortController(undefined);\n }\n\n if (timerRef.current !== undefined) {\n window.clearTimeout(timerRef.current);\n }\n\n const newTimer = window.setTimeout(() => handleTimer(), 100);\n setTimer(newTimer);\n }, [handleTimer]);\n\n const handleChange = useCallback(\n (values: string[]): void => {\n const result: T[] = [];\n for (const value of values) {\n let item = optionsRef.current?.find((option) => option.value === value)?.resource;\n if (!item && creatable !== false) {\n item = (onCreate as (input: string) => T)(value);\n }\n\n if (item) {\n result.push(item);\n }\n }\n onChange(result);\n },\n [creatable, onChange, onCreate]\n );\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent): void => {\n if (e.key === 'Enter') {\n if (!timerRef.current && !abortControllerRef.current) {\n killEvent(e);\n if (optionsRef.current && optionsRef.current.length > 0) {\n setOptions(optionsRef.current.slice(0, 1));\n handleChange([optionsRef.current[0].value]);\n }\n } else {\n // The user pressed enter, but we don't have results yet.\n // We need to wait for the results to come in.\n setAutoSubmit(true);\n }\n }\n },\n [handleChange]\n );\n\n const handleCreate = useCallback(\n (input: string): AsyncAutocompleteOption<T> => {\n const option = toOption((onCreate as (input: string) => T)(input));\n setOptions([...(optionsRef.current as AsyncAutocompleteOption<T>[]), option]);\n return option;\n },\n [onCreate, setOptions, toOption]\n );\n\n const handleFilter = useCallback((_value: string, selected: boolean) => !selected, []);\n\n useEffect(() => {\n return () => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n };\n }, []);\n\n return (\n <MultiSelect\n {...rest}\n ref={inputRef}\n defaultValue={defaultItems.map(toKey)}\n searchable\n onKeyDown={handleKeyDown}\n onSearchChange={handleSearchChange}\n data={options}\n onFocus={handleTimer}\n onChange={handleChange}\n onCreate={handleCreate}\n rightSectionWidth={40}\n rightSection={abortController ? <Loader size={16} /> : null}\n filter={handleFilter}\n creatable\n />\n );\n}\n\nfunction toDefaultItems<T>(defaultValue: T | T[] | undefined): T[] {\n if (!defaultValue) {\n return [];\n }\n if (Array.isArray(defaultValue)) {\n return defaultValue;\n }\n return [defaultValue];\n}\n"],"names":[],"mappings":";;;;AAmBM,SAAU,iBAAiB,CAAI,KAAgC,EAAA;IACnE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;AACrG,IAAA,MAAM,YAAY,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;AAClD,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAqB,SAAS,CAAC,CAAC;IAC1E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAAU,CAAC;IAC7C,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,EAAmB,CAAC;IAC1E,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,EAAW,CAAC;AACxD,IAAA,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAA+B,YAAY,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;AAElG,IAAA,MAAM,YAAY,GAAG,MAAM,EAAU,CAAC;AACtC,IAAA,YAAY,CAAC,OAAO,GAAG,SAAS,CAAC;AAEjC,IAAA,MAAM,QAAQ,GAAG,MAAM,EAAU,CAAC;AAClC,IAAA,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC;AAEzB,IAAA,MAAM,kBAAkB,GAAG,MAAM,EAAmB,CAAC;AACrD,IAAA,kBAAkB,CAAC,OAAO,GAAG,eAAe,CAAC;AAE7C,IAAA,MAAM,aAAa,GAAG,MAAM,EAAW,CAAC;AACxC,IAAA,aAAa,CAAC,OAAO,GAAG,UAAU,CAAC;AAEnC,IAAA,MAAM,UAAU,GAAG,MAAM,EAAgC,CAAC;AAC1D,IAAA,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;AAE7B,IAAA,MAAM,WAAW,GAAG,WAAW,CAAC,MAAW;QACzC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAEpB,QAAA,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACpD,QAAA,IAAI,KAAK,KAAK,YAAY,CAAC,OAAO,EAAE;;YAElC,OAAO;AACR,SAAA;QAED,YAAY,CAAC,KAAK,CAAC,CAAC;AAEpB,QAAA,MAAM,kBAAkB,GAAG,IAAI,eAAe,EAAE,CAAC;QACjD,kBAAkB,CAAC,kBAAkB,CAAC,CAAC;AAEvC,QAAA,WAAW,CAAC,KAAK,EAAE,kBAAkB,CAAC,MAAM,CAAC;AAC1C,aAAA,IAAI,CAAC,CAAC,SAAc,KAAI;AACvB,YAAA,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,OAAO,EAAE;gBACtC,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACpC,kBAAkB,CAAC,SAAS,CAAC,CAAC;gBAC9B,IAAI,aAAa,CAAC,OAAO,EAAE;AACzB,oBAAA,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE;wBACxB,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACjC,qBAAA;oBACD,aAAa,CAAC,KAAK,CAAC,CAAC;AACtB,iBAAA;AACF,aAAA;AACH,SAAC,CAAC;AACD,aAAA,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;KACvB,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;AAEtC,IAAA,MAAM,kBAAkB,GAAG,WAAW,CAAC,MAAW;QAChD,IAAI,kBAAkB,CAAC,OAAO,EAAE;AAC9B,YAAA,kBAAkB,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACnC,kBAAkB,CAAC,SAAS,CAAC,CAAC;AAC/B,SAAA;AAED,QAAA,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,EAAE;AAClC,YAAA,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACvC,SAAA;AAED,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC;QAC7D,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACrB,KAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;AAElB,IAAA,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,MAAgB,KAAU;QACzB,MAAM,MAAM,GAAQ,EAAE,CAAC;AACvB,QAAA,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;YAC1B,IAAI,IAAI,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC,EAAE,QAAQ,CAAC;AAClF,YAAA,IAAI,CAAC,IAAI,IAAI,SAAS,KAAK,KAAK,EAAE;AAChC,gBAAA,IAAI,GAAI,QAAiC,CAAC,KAAK,CAAC,CAAC;AAClD,aAAA;AAED,YAAA,IAAI,IAAI,EAAE;AACR,gBAAA,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnB,aAAA;AACF,SAAA;QACD,QAAQ,CAAC,MAAM,CAAC,CAAC;KAClB,EACD,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAChC,CAAC;AAEF,IAAA,MAAM,aAAa,GAAG,WAAW,CAC/B,CAAC,CAAsB,KAAU;AAC/B,QAAA,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE;YACrB,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE;gBACpD,SAAS,CAAC,CAAC,CAAC,CAAC;gBACb,IAAI,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;AACvD,oBAAA,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC3C,oBAAA,YAAY,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7C,iBAAA;AACF,aAAA;AAAM,iBAAA;;;gBAGL,aAAa,CAAC,IAAI,CAAC,CAAC;AACrB,aAAA;AACF,SAAA;AACH,KAAC,EACD,CAAC,YAAY,CAAC,CACf,CAAC;AAEF,IAAA,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,KAAa,KAAgC;QAC5C,MAAM,MAAM,GAAG,QAAQ,CAAE,QAAiC,CAAC,KAAK,CAAC,CAAC,CAAC;QACnE,UAAU,CAAC,CAAC,GAAI,UAAU,CAAC,OAAwC,EAAE,MAAM,CAAC,CAAC,CAAC;AAC9E,QAAA,OAAO,MAAM,CAAC;KACf,EACD,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,CACjC,CAAC;AAEF,IAAA,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,MAAc,EAAE,QAAiB,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAEvF,SAAS,CAAC,MAAK;AACb,QAAA,OAAO,MAAK;YACV,IAAI,kBAAkB,CAAC,OAAO,EAAE;AAC9B,gBAAA,kBAAkB,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;AACpC,aAAA;AACH,SAAC,CAAC;KACH,EAAE,EAAE,CAAC,CAAC;AAEP,IAAA,QACE,KAAA,CAAA,aAAA,CAAC,WAAW,EAAA,EAAA,GACN,IAAI,EACR,GAAG,EAAE,QAAQ,EACb,YAAY,EAAE,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EACrC,UAAU,EACV,IAAA,EAAA,SAAS,EAAE,aAAa,EACxB,cAAc,EAAE,kBAAkB,EAClC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,YAAY,EACtB,iBAAiB,EAAE,EAAE,EACrB,YAAY,EAAE,eAAe,GAAG,KAAA,CAAA,aAAA,CAAC,MAAM,EAAA,EAAC,IAAI,EAAE,EAAE,EAAI,CAAA,GAAG,IAAI,EAC3D,MAAM,EAAE,YAAY,EACpB,SAAS,EAAA,IAAA,EAAA,CACT,EACF;AACJ,CAAC;AAED,SAAS,cAAc,CAAI,YAAiC,EAAA;IAC1D,IAAI,CAAC,YAAY,EAAE;AACjB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AACD,IAAA,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE;AAC/B,QAAA,OAAO,YAAY,CAAC;AACrB,KAAA;IACD,OAAO,CAAC,YAAY,CAAC,CAAC;AACxB;;;;"}
@@ -18,8 +18,7 @@ function AttachmentButton(props) {
18
18
  }
19
19
  /**
20
20
  * Processes a single file.
21
- *
22
- * @param {File} file The file descriptor.
21
+ * @param file The file descriptor.
23
22
  */
24
23
  function processFile(file) {
25
24
  if (!file) {
@@ -1 +1 @@
1
- {"version":3,"file":"AttachmentButton.mjs","sources":["../../../src/AttachmentButton/AttachmentButton.tsx"],"sourcesContent":["import { Attachment, Binary, OperationOutcome } from '@medplum/fhirtypes';\nimport React, { useRef } from 'react';\nimport { useMedplum } from '../MedplumProvider/MedplumProvider';\nimport { killEvent } from '../utils/dom';\n\nexport interface AttachmentButtonProps {\n onUpload: (attachment: Attachment) => void;\n onUploadStart?: () => void;\n onUploadProgress?: (e: ProgressEvent) => void;\n children(props: { onClick(e: React.MouseEvent): void }): React.ReactNode;\n}\n\nexport function AttachmentButton(props: AttachmentButtonProps): JSX.Element {\n const medplum = useMedplum();\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n function onClick(e: React.MouseEvent): void {\n killEvent(e);\n fileInputRef.current?.click();\n }\n\n function onFileChange(e: React.ChangeEvent): void {\n killEvent(e);\n const files = (e.target as HTMLInputElement).files;\n if (files) {\n Array.from(files).forEach(processFile);\n }\n }\n\n /**\n * Processes a single file.\n *\n * @param {File} file The file descriptor.\n */\n function processFile(file: File): void {\n if (!file) {\n return;\n }\n\n const fileName = file.name;\n if (!fileName) {\n return;\n }\n\n if (props.onUploadStart) {\n props.onUploadStart();\n }\n\n const filename = file.name;\n const contentType = file.type || 'application/octet-stream';\n medplum\n .createBinary(file, filename, contentType, props.onUploadProgress)\n .then((binary: Binary) => {\n props.onUpload({\n contentType: binary.contentType,\n url: binary.url,\n title: filename,\n });\n })\n .catch((outcome: OperationOutcome) => {\n alert(outcome?.issue?.[0]?.details?.text);\n });\n }\n\n return (\n <>\n <input\n type=\"file\"\n data-testid=\"upload-file-input\"\n style={{ display: 'none' }}\n ref={fileInputRef}\n onChange={(e) => onFileChange(e)}\n />\n {props.children({ onClick })}\n </>\n );\n}\n"],"names":[],"mappings":";;;;AAYM,SAAU,gBAAgB,CAAC,KAA4B,EAAA;AAC3D,IAAA,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;AAC7B,IAAA,MAAM,YAAY,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IAEpD,SAAS,OAAO,CAAC,CAAmB,EAAA;QAClC,SAAS,CAAC,CAAC,CAAC,CAAC;AACb,QAAA,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;KAC/B;IAED,SAAS,YAAY,CAAC,CAAoB,EAAA;QACxC,SAAS,CAAC,CAAC,CAAC,CAAC;AACb,QAAA,MAAM,KAAK,GAAI,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC;AACnD,QAAA,IAAI,KAAK,EAAE;YACT,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;AACxC,SAAA;KACF;AAED;;;;AAIG;IACH,SAAS,WAAW,CAAC,IAAU,EAAA;QAC7B,IAAI,CAAC,IAAI,EAAE;YACT,OAAO;AACR,SAAA;AAED,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;QAC3B,IAAI,CAAC,QAAQ,EAAE;YACb,OAAO;AACR,SAAA;QAED,IAAI,KAAK,CAAC,aAAa,EAAE;YACvB,KAAK,CAAC,aAAa,EAAE,CAAC;AACvB,SAAA;AAED,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;AAC3B,QAAA,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,IAAI,0BAA0B,CAAC;QAC5D,OAAO;aACJ,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,CAAC,gBAAgB,CAAC;AACjE,aAAA,IAAI,CAAC,CAAC,MAAc,KAAI;YACvB,KAAK,CAAC,QAAQ,CAAC;gBACb,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,GAAG,EAAE,MAAM,CAAC,GAAG;AACf,gBAAA,KAAK,EAAE,QAAQ;AAChB,aAAA,CAAC,CAAC;AACL,SAAC,CAAC;AACD,aAAA,KAAK,CAAC,CAAC,OAAyB,KAAI;AACnC,YAAA,KAAK,CAAC,OAAO,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAC5C,SAAC,CAAC,CAAC;KACN;AAED,IAAA,QACE,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA;AACE,QAAA,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,EACE,IAAI,EAAC,MAAM,EAAA,aAAA,EACC,mBAAmB,EAC/B,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAC1B,GAAG,EAAE,YAAY,EACjB,QAAQ,EAAE,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,EAChC,CAAA;QACD,KAAK,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC,CAC3B,EACH;AACJ;;;;"}
1
+ {"version":3,"file":"AttachmentButton.mjs","sources":["../../../src/AttachmentButton/AttachmentButton.tsx"],"sourcesContent":["import { Attachment, Binary, OperationOutcome } from '@medplum/fhirtypes';\nimport React, { useRef } from 'react';\nimport { useMedplum } from '../MedplumProvider/MedplumProvider';\nimport { killEvent } from '../utils/dom';\n\nexport interface AttachmentButtonProps {\n onUpload: (attachment: Attachment) => void;\n onUploadStart?: () => void;\n onUploadProgress?: (e: ProgressEvent) => void;\n children(props: { onClick(e: React.MouseEvent): void }): React.ReactNode;\n}\n\nexport function AttachmentButton(props: AttachmentButtonProps): JSX.Element {\n const medplum = useMedplum();\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n function onClick(e: React.MouseEvent): void {\n killEvent(e);\n fileInputRef.current?.click();\n }\n\n function onFileChange(e: React.ChangeEvent): void {\n killEvent(e);\n const files = (e.target as HTMLInputElement).files;\n if (files) {\n Array.from(files).forEach(processFile);\n }\n }\n\n /**\n * Processes a single file.\n * @param file The file descriptor.\n */\n function processFile(file: File): void {\n if (!file) {\n return;\n }\n\n const fileName = file.name;\n if (!fileName) {\n return;\n }\n\n if (props.onUploadStart) {\n props.onUploadStart();\n }\n\n const filename = file.name;\n const contentType = file.type || 'application/octet-stream';\n medplum\n .createBinary(file, filename, contentType, props.onUploadProgress)\n .then((binary: Binary) => {\n props.onUpload({\n contentType: binary.contentType,\n url: binary.url,\n title: filename,\n });\n })\n .catch((outcome: OperationOutcome) => {\n alert(outcome?.issue?.[0]?.details?.text);\n });\n }\n\n return (\n <>\n <input\n type=\"file\"\n data-testid=\"upload-file-input\"\n style={{ display: 'none' }}\n ref={fileInputRef}\n onChange={(e) => onFileChange(e)}\n />\n {props.children({ onClick })}\n </>\n );\n}\n"],"names":[],"mappings":";;;;AAYM,SAAU,gBAAgB,CAAC,KAA4B,EAAA;AAC3D,IAAA,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;AAC7B,IAAA,MAAM,YAAY,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IAEpD,SAAS,OAAO,CAAC,CAAmB,EAAA;QAClC,SAAS,CAAC,CAAC,CAAC,CAAC;AACb,QAAA,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;KAC/B;IAED,SAAS,YAAY,CAAC,CAAoB,EAAA;QACxC,SAAS,CAAC,CAAC,CAAC,CAAC;AACb,QAAA,MAAM,KAAK,GAAI,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC;AACnD,QAAA,IAAI,KAAK,EAAE;YACT,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;AACxC,SAAA;KACF;AAED;;;AAGG;IACH,SAAS,WAAW,CAAC,IAAU,EAAA;QAC7B,IAAI,CAAC,IAAI,EAAE;YACT,OAAO;AACR,SAAA;AAED,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;QAC3B,IAAI,CAAC,QAAQ,EAAE;YACb,OAAO;AACR,SAAA;QAED,IAAI,KAAK,CAAC,aAAa,EAAE;YACvB,KAAK,CAAC,aAAa,EAAE,CAAC;AACvB,SAAA;AAED,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;AAC3B,QAAA,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,IAAI,0BAA0B,CAAC;QAC5D,OAAO;aACJ,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,CAAC,gBAAgB,CAAC;AACjE,aAAA,IAAI,CAAC,CAAC,MAAc,KAAI;YACvB,KAAK,CAAC,QAAQ,CAAC;gBACb,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,GAAG,EAAE,MAAM,CAAC,GAAG;AACf,gBAAA,KAAK,EAAE,QAAQ;AAChB,aAAA,CAAC,CAAC;AACL,SAAC,CAAC;AACD,aAAA,KAAK,CAAC,CAAC,OAAyB,KAAI;AACnC,YAAA,KAAK,CAAC,OAAO,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAC5C,SAAC,CAAC,CAAC;KACN;AAED,IAAA,QACE,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA;AACE,QAAA,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,EACE,IAAI,EAAC,MAAM,EAAA,aAAA,EACC,mBAAmB,EAC/B,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAC1B,GAAG,EAAE,YAAY,EACjB,QAAQ,EAAE,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,EAChC,CAAA;QACD,KAAK,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC,CAC3B,EACH;AACJ;;;;"}
@@ -61,6 +61,7 @@ function convertLocalToIso(localString) {
61
61
  * Returns the input type for the requested type.
62
62
  * JSDOM does not support many of the valid <input> type attributes.
63
63
  * For example, it won't fire change events for <input type="datetime-local">.
64
+ * @returns The input type for the current environment.
64
65
  */
65
66
  function getInputType() {
66
67
  return 'datetime-local';
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimeInput.mjs","sources":["../../../src/DateTimeInput/DateTimeInput.tsx"],"sourcesContent":["import { TextInput } from '@mantine/core';\nimport { isValidDate } from '@medplum/core';\nimport { OperationOutcome } from '@medplum/fhirtypes';\nimport React from 'react';\nimport { getErrorsForInput } from '../utils/outcomes';\n\nexport interface DateTimeInputProps {\n name?: string;\n placeholder?: string;\n defaultValue?: string;\n required?: boolean;\n outcome?: OperationOutcome;\n onChange?: (value: string) => void;\n}\n\n/**\n * The DateTimeInput component is a wrapper around the HTML5 input type=\"datetime-local\".\n * The main purpose is to reconcile time zones.\n * Most of our date/time values are in ISO-8601, which includes a time zone offset.\n * The datetime-local input does not support the time zone offset.\n * @param props The Input props.\n * @returns The JSX element to render.\n */\nexport function DateTimeInput(props: DateTimeInputProps): JSX.Element {\n return (\n <TextInput\n id={props.name}\n name={props.name}\n data-testid={props.name}\n placeholder={props.placeholder}\n required={props.required}\n type={getInputType()}\n defaultValue={convertIsoToLocal(props.defaultValue)}\n error={getErrorsForInput(props.outcome, props.name)}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => {\n if (props.onChange) {\n const newValue = e.currentTarget.value;\n props.onChange(convertLocalToIso(newValue));\n }\n }}\n />\n );\n}\n\n/**\n * Converts an ISO-8601 date/time string to a local date/time string.\n * @param isoString The ISO-8601 date/time string to convert.\n * @returns The local date/time string.\n */\nexport function convertIsoToLocal(isoString: string | undefined): string {\n if (!isoString) {\n return '';\n }\n\n // Convert the ISO-8601 date to a local datetime-local value.\n // See: https://developer.mozilla.org/en-US/docs/Web/HTML/Date_and_time_formats#local_date_and_time_strings\n // See: https://stackoverflow.com/a/60368477\n const date = new Date(isoString);\n if (!isValidDate(date)) {\n // If the input string was malformed, return an empty string.\n return '';\n }\n\n return date.toLocaleDateString('sv') + 'T' + date.toLocaleTimeString('sv');\n}\n\n/**\n * Converts a local date/time string to an ISO-8601 date/time string.\n * @param localString The local date/time string to convert.\n * @returns The ISO-8601 date/time string.\n */\nexport function convertLocalToIso(localString: string | undefined): string {\n if (!localString) {\n return '';\n }\n\n // Try to parse the local string as a Date\n // JavaScript's Date() constructor defaults to the local time zone.\n // The Date() constructor will throw if the value is malformed.\n const date = new Date(localString);\n if (!isValidDate(date)) {\n // If the input string was malformed, return an empty string.\n return '';\n }\n\n return date.toISOString();\n}\n\n/**\n * Returns the input type for the requested type.\n * JSDOM does not support many of the valid <input> type attributes.\n * For example, it won't fire change events for <input type=\"datetime-local\">.\n */\nfunction getInputType(): string {\n return process.env.NODE_ENV === 'test' ? 'text' : 'datetime-local';\n}\n"],"names":[],"mappings":";;;;;AAeA;;;;;;;AAOG;AACG,SAAU,aAAa,CAAC,KAAyB,EAAA;AACrD,IAAA,QACE,KAAA,CAAA,aAAA,CAAC,SAAS,EAAA,EACR,EAAE,EAAE,KAAK,CAAC,IAAI,EACd,IAAI,EAAE,KAAK,CAAC,IAAI,EACH,aAAA,EAAA,KAAK,CAAC,IAAI,EACvB,WAAW,EAAE,KAAK,CAAC,WAAW,EAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,IAAI,EAAE,YAAY,EAAE,EACpB,YAAY,EAAE,iBAAiB,CAAC,KAAK,CAAC,YAAY,CAAC,EACnD,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,EACnD,QAAQ,EAAE,CAAC,CAAsC,KAAI;YACnD,IAAI,KAAK,CAAC,QAAQ,EAAE;AAClB,gBAAA,MAAM,QAAQ,GAAG,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC;gBACvC,KAAK,CAAC,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC7C,aAAA;SACF,EAAA,CACD,EACF;AACJ,CAAC;AAED;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,SAA6B,EAAA;IAC7D,IAAI,CAAC,SAAS,EAAE;AACd,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;;;;AAKD,IAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;AACjC,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;;AAEtB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AAED,IAAA,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAC7E,CAAC;AAED;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,WAA+B,EAAA;IAC/D,IAAI,CAAC,WAAW,EAAE;AAChB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;;;;AAKD,IAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC;AACnC,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;;AAEtB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AAED,IAAA,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED;;;;AAIG;AACH,SAAS,YAAY,GAAA;AACnB,IAAA,OAAkD,gBAAgB,CAAC;AACrE;;;;"}
1
+ {"version":3,"file":"DateTimeInput.mjs","sources":["../../../src/DateTimeInput/DateTimeInput.tsx"],"sourcesContent":["import { TextInput } from '@mantine/core';\nimport { isValidDate } from '@medplum/core';\nimport { OperationOutcome } from '@medplum/fhirtypes';\nimport React from 'react';\nimport { getErrorsForInput } from '../utils/outcomes';\n\nexport interface DateTimeInputProps {\n name?: string;\n placeholder?: string;\n defaultValue?: string;\n required?: boolean;\n outcome?: OperationOutcome;\n onChange?: (value: string) => void;\n}\n\n/**\n * The DateTimeInput component is a wrapper around the HTML5 input type=\"datetime-local\".\n * The main purpose is to reconcile time zones.\n * Most of our date/time values are in ISO-8601, which includes a time zone offset.\n * The datetime-local input does not support the time zone offset.\n * @param props The Input props.\n * @returns The JSX element to render.\n */\nexport function DateTimeInput(props: DateTimeInputProps): JSX.Element {\n return (\n <TextInput\n id={props.name}\n name={props.name}\n data-testid={props.name}\n placeholder={props.placeholder}\n required={props.required}\n type={getInputType()}\n defaultValue={convertIsoToLocal(props.defaultValue)}\n error={getErrorsForInput(props.outcome, props.name)}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => {\n if (props.onChange) {\n const newValue = e.currentTarget.value;\n props.onChange(convertLocalToIso(newValue));\n }\n }}\n />\n );\n}\n\n/**\n * Converts an ISO-8601 date/time string to a local date/time string.\n * @param isoString The ISO-8601 date/time string to convert.\n * @returns The local date/time string.\n */\nexport function convertIsoToLocal(isoString: string | undefined): string {\n if (!isoString) {\n return '';\n }\n\n // Convert the ISO-8601 date to a local datetime-local value.\n // See: https://developer.mozilla.org/en-US/docs/Web/HTML/Date_and_time_formats#local_date_and_time_strings\n // See: https://stackoverflow.com/a/60368477\n const date = new Date(isoString);\n if (!isValidDate(date)) {\n // If the input string was malformed, return an empty string.\n return '';\n }\n\n return date.toLocaleDateString('sv') + 'T' + date.toLocaleTimeString('sv');\n}\n\n/**\n * Converts a local date/time string to an ISO-8601 date/time string.\n * @param localString The local date/time string to convert.\n * @returns The ISO-8601 date/time string.\n */\nexport function convertLocalToIso(localString: string | undefined): string {\n if (!localString) {\n return '';\n }\n\n // Try to parse the local string as a Date\n // JavaScript's Date() constructor defaults to the local time zone.\n // The Date() constructor will throw if the value is malformed.\n const date = new Date(localString);\n if (!isValidDate(date)) {\n // If the input string was malformed, return an empty string.\n return '';\n }\n\n return date.toISOString();\n}\n\n/**\n * Returns the input type for the requested type.\n * JSDOM does not support many of the valid <input> type attributes.\n * For example, it won't fire change events for <input type=\"datetime-local\">.\n * @returns The input type for the current environment.\n */\nfunction getInputType(): string {\n return process.env.NODE_ENV === 'test' ? 'text' : 'datetime-local';\n}\n"],"names":[],"mappings":";;;;;AAeA;;;;;;;AAOG;AACG,SAAU,aAAa,CAAC,KAAyB,EAAA;AACrD,IAAA,QACE,KAAA,CAAA,aAAA,CAAC,SAAS,EAAA,EACR,EAAE,EAAE,KAAK,CAAC,IAAI,EACd,IAAI,EAAE,KAAK,CAAC,IAAI,EACH,aAAA,EAAA,KAAK,CAAC,IAAI,EACvB,WAAW,EAAE,KAAK,CAAC,WAAW,EAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,IAAI,EAAE,YAAY,EAAE,EACpB,YAAY,EAAE,iBAAiB,CAAC,KAAK,CAAC,YAAY,CAAC,EACnD,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,EACnD,QAAQ,EAAE,CAAC,CAAsC,KAAI;YACnD,IAAI,KAAK,CAAC,QAAQ,EAAE;AAClB,gBAAA,MAAM,QAAQ,GAAG,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC;gBACvC,KAAK,CAAC,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC7C,aAAA;SACF,EAAA,CACD,EACF;AACJ,CAAC;AAED;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,SAA6B,EAAA;IAC7D,IAAI,CAAC,SAAS,EAAE;AACd,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;;;;AAKD,IAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;AACjC,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;;AAEtB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AAED,IAAA,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAC7E,CAAC;AAED;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,WAA+B,EAAA;IAC/D,IAAI,CAAC,WAAW,EAAE;AAChB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;;;;AAKD,IAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC;AACnC,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;;AAEtB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AAED,IAAA,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;AAKG;AACH,SAAS,YAAY,GAAA;AACnB,IAAA,OAAkD,gBAAgB,CAAC;AACrE;;;;"}
@@ -8,6 +8,8 @@ import { killEvent, isCheckboxCell } from '../utils/dom.mjs';
8
8
 
9
9
  /**
10
10
  * The FhirPathTable component represents the embeddable search table control.
11
+ * @param props FhirPathTable React props.
12
+ * @returns FhirPathTable React node.
11
13
  */
12
14
  function FhirPathTable(props) {
13
15
  const medplum = useMedplum();
@@ -1 +1 @@
1
- {"version":3,"file":"FhirPathTable.mjs","sources":["../../../src/FhirPathTable/FhirPathTable.tsx"],"sourcesContent":["import { Button, Loader, Table } from '@mantine/core';\nimport { IndexedStructureDefinition, normalizeOperationOutcome, PropertyType } from '@medplum/core';\nimport { OperationOutcome, Resource } from '@medplum/fhirtypes';\nimport React, { useEffect, useRef, useState } from 'react';\nimport { FhirPathDisplay } from '../FhirPathDisplay/FhirPathDisplay';\nimport { useMedplum } from '../MedplumProvider/MedplumProvider';\nimport { SearchClickEvent } from '../SearchControl/SearchControl';\nimport { isCheckboxCell, killEvent } from '../utils/dom';\n\nexport interface FhirPathTableField {\n readonly propertyType: PropertyType;\n readonly name: string;\n readonly fhirPath: string;\n}\n\nexport interface FhirPathTableProps {\n resourceType: string;\n query: string;\n fields: FhirPathTableField[];\n checkboxesEnabled?: boolean;\n onClick?: (e: SearchClickEvent) => void;\n onAuxClick?: (e: SearchClickEvent) => void;\n onBulk?: (ids: string[]) => void;\n}\n\nexport interface SmartSearchResponse {\n data: {\n ResourceList: Resource[];\n };\n}\n\n/**\n * The FhirPathTable component represents the embeddable search table control.\n */\nexport function FhirPathTable(props: FhirPathTableProps): JSX.Element {\n const medplum = useMedplum();\n const [schema, setSchema] = useState<IndexedStructureDefinition | undefined>();\n const [outcome, setOutcome] = useState<OperationOutcome | undefined>();\n const { query, fields } = props;\n const [response, setResponse] = useState<SmartSearchResponse | undefined>();\n const [selected, setSelected] = useState<{ [id: string]: boolean }>({});\n\n const responseRef = useRef<SmartSearchResponse>();\n responseRef.current = response;\n\n const selectedRef = useRef<{ [id: string]: boolean }>({});\n selectedRef.current = selected;\n\n useEffect(() => {\n setOutcome(undefined);\n medplum\n .graphql(query)\n .then(setResponse)\n .catch((err) => setOutcome(normalizeOperationOutcome(err)));\n }, [medplum, query]);\n\n function handleSingleCheckboxClick(e: React.ChangeEvent, id: string): void {\n e.stopPropagation();\n\n const el = e.target as HTMLInputElement;\n const checked = el.checked;\n const newSelected = { ...selectedRef.current };\n if (checked) {\n newSelected[id] = true;\n } else {\n delete newSelected[id];\n }\n setSelected(newSelected);\n }\n\n function handleAllCheckboxClick(e: React.ChangeEvent): void {\n e.stopPropagation();\n\n const el = e.target as HTMLInputElement;\n const checked = el.checked;\n const newSelected = {} as { [id: string]: boolean };\n const resources = responseRef.current?.data?.ResourceList;\n if (checked && resources) {\n resources.forEach((resource) => {\n if (resource?.id) {\n newSelected[resource.id] = true;\n }\n });\n }\n setSelected(newSelected);\n }\n\n function isAllSelected(): boolean {\n const resources = responseRef.current?.data?.ResourceList;\n if (!resources || resources.length === 0) {\n return false;\n }\n for (const resource of resources) {\n if (resource?.id && !selectedRef.current[resource.id]) {\n return false;\n }\n }\n return true;\n }\n\n function handleRowClick(e: React.MouseEvent, resource: Resource): void {\n if (isCheckboxCell(e.target as Element)) {\n // Ignore clicks on checkboxes\n return;\n }\n\n killEvent(e);\n\n if (e.button !== 1 && props.onClick) {\n props.onClick(new SearchClickEvent(resource, e));\n }\n\n if (e.button === 1 && props.onAuxClick) {\n props.onAuxClick(new SearchClickEvent(resource, e));\n }\n }\n\n useEffect(() => {\n medplum\n .requestSchema(props.resourceType)\n .then((newSchema) => {\n // The schema could have the same object identity,\n // so need to use the spread operator to kick React re-render.\n setSchema({ ...newSchema });\n })\n .catch(console.log);\n }, [medplum, props.resourceType]);\n\n const typeSchema = schema?.types?.[props.resourceType];\n if (!typeSchema) {\n return <Loader />;\n }\n\n const checkboxColumn = props.checkboxesEnabled;\n\n return (\n <div onContextMenu={(e) => killEvent(e)} data-testid=\"search-control\">\n <Table>\n <thead>\n <tr>\n {checkboxColumn && (\n <th>\n <input\n type=\"checkbox\"\n value=\"checked\"\n aria-label=\"all-checkbox\"\n data-testid=\"all-checkbox\"\n checked={isAllSelected()}\n onChange={(e) => handleAllCheckboxClick(e)}\n />\n </th>\n )}\n {fields.map((field) => (\n <th key={field.name}>{field.name}</th>\n ))}\n </tr>\n </thead>\n <tbody>\n {response?.data?.ResourceList?.map(\n (resource) =>\n resource && (\n <tr\n key={resource.id}\n data-testid=\"search-control-row\"\n onClick={(e) => handleRowClick(e, resource)}\n onAuxClick={(e) => handleRowClick(e, resource)}\n >\n {checkboxColumn && (\n <td>\n <input\n type=\"checkbox\"\n value=\"checked\"\n data-testid=\"row-checkbox\"\n aria-label={`Checkbox for ${resource.id}`}\n checked={!!selected[resource.id as string]}\n onChange={(e) => handleSingleCheckboxClick(e, resource.id as string)}\n />\n </td>\n )}\n {fields.map((field) => {\n return (\n <td key={field.name}>\n <FhirPathDisplay propertyType={field.propertyType} path={field.fhirPath} resource={resource} />\n </td>\n );\n })}\n </tr>\n )\n )}\n </tbody>\n </Table>\n {response?.data?.ResourceList?.length === 0 && <div data-testid=\"empty-search\">No results</div>}\n {outcome && (\n <div data-testid=\"search-error\">\n <pre style={{ textAlign: 'left' }}>{JSON.stringify(outcome, undefined, 2)}</pre>\n </div>\n )}\n {props.onBulk && (\n <Button onClick={() => (props.onBulk as (ids: string[]) => any)(Object.keys(selectedRef.current))}>\n Bulk...\n </Button>\n )}\n </div>\n );\n}\n\nexport const MemoizedFhirPathTable = React.memo(FhirPathTable);\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FhirPathTable.mjs","sources":["../../../src/FhirPathTable/FhirPathTable.tsx"],"sourcesContent":["import { Button, Loader, Table } from '@mantine/core';\nimport { IndexedStructureDefinition, normalizeOperationOutcome, PropertyType } from '@medplum/core';\nimport { OperationOutcome, Resource } from '@medplum/fhirtypes';\nimport React, { useEffect, useRef, useState } from 'react';\nimport { FhirPathDisplay } from '../FhirPathDisplay/FhirPathDisplay';\nimport { useMedplum } from '../MedplumProvider/MedplumProvider';\nimport { SearchClickEvent } from '../SearchControl/SearchControl';\nimport { isCheckboxCell, killEvent } from '../utils/dom';\n\nexport interface FhirPathTableField {\n readonly propertyType: PropertyType;\n readonly name: string;\n readonly fhirPath: string;\n}\n\nexport interface FhirPathTableProps {\n resourceType: string;\n query: string;\n fields: FhirPathTableField[];\n checkboxesEnabled?: boolean;\n onClick?: (e: SearchClickEvent) => void;\n onAuxClick?: (e: SearchClickEvent) => void;\n onBulk?: (ids: string[]) => void;\n}\n\nexport interface SmartSearchResponse {\n data: {\n ResourceList: Resource[];\n };\n}\n\n/**\n * The FhirPathTable component represents the embeddable search table control.\n * @param props FhirPathTable React props.\n * @returns FhirPathTable React node.\n */\nexport function FhirPathTable(props: FhirPathTableProps): JSX.Element {\n const medplum = useMedplum();\n const [schema, setSchema] = useState<IndexedStructureDefinition | undefined>();\n const [outcome, setOutcome] = useState<OperationOutcome | undefined>();\n const { query, fields } = props;\n const [response, setResponse] = useState<SmartSearchResponse | undefined>();\n const [selected, setSelected] = useState<{ [id: string]: boolean }>({});\n\n const responseRef = useRef<SmartSearchResponse>();\n responseRef.current = response;\n\n const selectedRef = useRef<{ [id: string]: boolean }>({});\n selectedRef.current = selected;\n\n useEffect(() => {\n setOutcome(undefined);\n medplum\n .graphql(query)\n .then(setResponse)\n .catch((err) => setOutcome(normalizeOperationOutcome(err)));\n }, [medplum, query]);\n\n function handleSingleCheckboxClick(e: React.ChangeEvent, id: string): void {\n e.stopPropagation();\n\n const el = e.target as HTMLInputElement;\n const checked = el.checked;\n const newSelected = { ...selectedRef.current };\n if (checked) {\n newSelected[id] = true;\n } else {\n delete newSelected[id];\n }\n setSelected(newSelected);\n }\n\n function handleAllCheckboxClick(e: React.ChangeEvent): void {\n e.stopPropagation();\n\n const el = e.target as HTMLInputElement;\n const checked = el.checked;\n const newSelected = {} as { [id: string]: boolean };\n const resources = responseRef.current?.data?.ResourceList;\n if (checked && resources) {\n resources.forEach((resource) => {\n if (resource?.id) {\n newSelected[resource.id] = true;\n }\n });\n }\n setSelected(newSelected);\n }\n\n function isAllSelected(): boolean {\n const resources = responseRef.current?.data?.ResourceList;\n if (!resources || resources.length === 0) {\n return false;\n }\n for (const resource of resources) {\n if (resource?.id && !selectedRef.current[resource.id]) {\n return false;\n }\n }\n return true;\n }\n\n function handleRowClick(e: React.MouseEvent, resource: Resource): void {\n if (isCheckboxCell(e.target as Element)) {\n // Ignore clicks on checkboxes\n return;\n }\n\n killEvent(e);\n\n if (e.button !== 1 && props.onClick) {\n props.onClick(new SearchClickEvent(resource, e));\n }\n\n if (e.button === 1 && props.onAuxClick) {\n props.onAuxClick(new SearchClickEvent(resource, e));\n }\n }\n\n useEffect(() => {\n medplum\n .requestSchema(props.resourceType)\n .then((newSchema) => {\n // The schema could have the same object identity,\n // so need to use the spread operator to kick React re-render.\n setSchema({ ...newSchema });\n })\n .catch(console.log);\n }, [medplum, props.resourceType]);\n\n const typeSchema = schema?.types?.[props.resourceType];\n if (!typeSchema) {\n return <Loader />;\n }\n\n const checkboxColumn = props.checkboxesEnabled;\n\n return (\n <div onContextMenu={(e) => killEvent(e)} data-testid=\"search-control\">\n <Table>\n <thead>\n <tr>\n {checkboxColumn && (\n <th>\n <input\n type=\"checkbox\"\n value=\"checked\"\n aria-label=\"all-checkbox\"\n data-testid=\"all-checkbox\"\n checked={isAllSelected()}\n onChange={(e) => handleAllCheckboxClick(e)}\n />\n </th>\n )}\n {fields.map((field) => (\n <th key={field.name}>{field.name}</th>\n ))}\n </tr>\n </thead>\n <tbody>\n {response?.data?.ResourceList?.map(\n (resource) =>\n resource && (\n <tr\n key={resource.id}\n data-testid=\"search-control-row\"\n onClick={(e) => handleRowClick(e, resource)}\n onAuxClick={(e) => handleRowClick(e, resource)}\n >\n {checkboxColumn && (\n <td>\n <input\n type=\"checkbox\"\n value=\"checked\"\n data-testid=\"row-checkbox\"\n aria-label={`Checkbox for ${resource.id}`}\n checked={!!selected[resource.id as string]}\n onChange={(e) => handleSingleCheckboxClick(e, resource.id as string)}\n />\n </td>\n )}\n {fields.map((field) => {\n return (\n <td key={field.name}>\n <FhirPathDisplay propertyType={field.propertyType} path={field.fhirPath} resource={resource} />\n </td>\n );\n })}\n </tr>\n )\n )}\n </tbody>\n </Table>\n {response?.data?.ResourceList?.length === 0 && <div data-testid=\"empty-search\">No results</div>}\n {outcome && (\n <div data-testid=\"search-error\">\n <pre style={{ textAlign: 'left' }}>{JSON.stringify(outcome, undefined, 2)}</pre>\n </div>\n )}\n {props.onBulk && (\n <Button onClick={() => (props.onBulk as (ids: string[]) => any)(Object.keys(selectedRef.current))}>\n Bulk...\n </Button>\n )}\n </div>\n );\n}\n\nexport const MemoizedFhirPathTable = React.memo(FhirPathTable);\n"],"names":[],"mappings":";;;;;;;;AA+BA;;;;AAIG;AACG,SAAU,aAAa,CAAC,KAAyB,EAAA;AACrD,IAAA,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,EAA0C,CAAC;IAC/E,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,EAAgC,CAAC;AACvE,IAAA,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAChC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,EAAmC,CAAC;IAC5E,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAA4B,EAAE,CAAC,CAAC;AAExE,IAAA,MAAM,WAAW,GAAG,MAAM,EAAuB,CAAC;AAClD,IAAA,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;AAE/B,IAAA,MAAM,WAAW,GAAG,MAAM,CAA4B,EAAE,CAAC,CAAC;AAC1D,IAAA,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IAE/B,SAAS,CAAC,MAAK;QACb,UAAU,CAAC,SAAS,CAAC,CAAC;QACtB,OAAO;aACJ,OAAO,CAAC,KAAK,CAAC;aACd,IAAI,CAAC,WAAW,CAAC;AACjB,aAAA,KAAK,CAAC,CAAC,GAAG,KAAK,UAAU,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAChE,KAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;AAErB,IAAA,SAAS,yBAAyB,CAAC,CAAoB,EAAE,EAAU,EAAA;QACjE,CAAC,CAAC,eAAe,EAAE,CAAC;AAEpB,QAAA,MAAM,EAAE,GAAG,CAAC,CAAC,MAA0B,CAAC;AACxC,QAAA,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC;QAC3B,MAAM,WAAW,GAAG,EAAE,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC;AAC/C,QAAA,IAAI,OAAO,EAAE;AACX,YAAA,WAAW,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;AACxB,SAAA;AAAM,aAAA;AACL,YAAA,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC;AACxB,SAAA;QACD,WAAW,CAAC,WAAW,CAAC,CAAC;KAC1B;IAED,SAAS,sBAAsB,CAAC,CAAoB,EAAA;QAClD,CAAC,CAAC,eAAe,EAAE,CAAC;AAEpB,QAAA,MAAM,EAAE,GAAG,CAAC,CAAC,MAA0B,CAAC;AACxC,QAAA,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC;QAC3B,MAAM,WAAW,GAAG,EAA+B,CAAC;QACpD,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC;QAC1D,IAAI,OAAO,IAAI,SAAS,EAAE;AACxB,YAAA,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAI;gBAC7B,IAAI,QAAQ,EAAE,EAAE,EAAE;AAChB,oBAAA,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;AACjC,iBAAA;AACH,aAAC,CAAC,CAAC;AACJ,SAAA;QACD,WAAW,CAAC,WAAW,CAAC,CAAC;KAC1B;AAED,IAAA,SAAS,aAAa,GAAA;QACpB,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC;QAC1D,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE;AACxC,YAAA,OAAO,KAAK,CAAC;AACd,SAAA;AACD,QAAA,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE;AAChC,YAAA,IAAI,QAAQ,EAAE,EAAE,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;AACrD,gBAAA,OAAO,KAAK,CAAC;AACd,aAAA;AACF,SAAA;AACD,QAAA,OAAO,IAAI,CAAC;KACb;AAED,IAAA,SAAS,cAAc,CAAC,CAAmB,EAAE,QAAkB,EAAA;AAC7D,QAAA,IAAI,cAAc,CAAC,CAAC,CAAC,MAAiB,CAAC,EAAE;;YAEvC,OAAO;AACR,SAAA;QAED,SAAS,CAAC,CAAC,CAAC,CAAC;QAEb,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE;YACnC,KAAK,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;AAClD,SAAA;QAED,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,EAAE;YACtC,KAAK,CAAC,UAAU,CAAC,IAAI,gBAAgB,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;AACrD,SAAA;KACF;IAED,SAAS,CAAC,MAAK;QACb,OAAO;AACJ,aAAA,aAAa,CAAC,KAAK,CAAC,YAAY,CAAC;AACjC,aAAA,IAAI,CAAC,CAAC,SAAS,KAAI;;;AAGlB,YAAA,SAAS,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC,CAAC;AAC9B,SAAC,CAAC;AACD,aAAA,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;KACvB,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;IAElC,MAAM,UAAU,GAAG,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;IACvD,IAAI,CAAC,UAAU,EAAE;QACf,OAAO,KAAA,CAAA,aAAA,CAAC,MAAM,EAAA,IAAA,CAAG,CAAC;AACnB,KAAA;AAED,IAAA,MAAM,cAAc,GAAG,KAAK,CAAC,iBAAiB,CAAC;AAE/C,IAAA,QACE,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAK,aAAa,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAc,gBAAgB,EAAA;AACnE,QAAA,KAAA,CAAA,aAAA,CAAC,KAAK,EAAA,IAAA;AACJ,YAAA,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,IAAA;AACE,gBAAA,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,IAAA;AACG,oBAAA,cAAc,KACb,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,IAAA;AACE,wBAAA,KAAA,CAAA,aAAA,CAAA,OAAA,EAAA,EACE,IAAI,EAAC,UAAU,EACf,KAAK,EAAC,SAAS,EAAA,YAAA,EACJ,cAAc,EAAA,aAAA,EACb,cAAc,EAC1B,OAAO,EAAE,aAAa,EAAE,EACxB,QAAQ,EAAE,CAAC,CAAC,KAAK,sBAAsB,CAAC,CAAC,CAAC,EAAA,CAC1C,CACC,CACN;oBACA,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,MAChB,KAAI,CAAA,aAAA,CAAA,IAAA,EAAA,EAAA,GAAG,EAAE,KAAK,CAAC,IAAI,EAAA,EAAG,KAAK,CAAC,IAAI,CAAM,CACvC,CAAC,CACC,CACC;YACR,KACG,CAAA,aAAA,CAAA,OAAA,EAAA,IAAA,EAAA,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,CAChC,CAAC,QAAQ,KACP,QAAQ,KACN,KACE,CAAA,aAAA,CAAA,IAAA,EAAA,EAAA,GAAG,EAAE,QAAQ,CAAC,EAAE,EAAA,aAAA,EACJ,oBAAoB,EAChC,OAAO,EAAE,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,EAAE,QAAQ,CAAC,EAC3C,UAAU,EAAE,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAA;AAE7C,gBAAA,cAAc,KACb,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,IAAA;oBACE,KACE,CAAA,aAAA,CAAA,OAAA,EAAA,EAAA,IAAI,EAAC,UAAU,EACf,KAAK,EAAC,SAAS,iBACH,cAAc,EAAA,YAAA,EACd,gBAAgB,QAAQ,CAAC,EAAE,CAAE,CAAA,EACzC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAY,CAAC,EAC1C,QAAQ,EAAE,CAAC,CAAC,KAAK,yBAAyB,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAY,CAAC,EAAA,CACpE,CACC,CACN;AACA,gBAAA,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,KAAI;AACpB,oBAAA,QACE,KAAI,CAAA,aAAA,CAAA,IAAA,EAAA,EAAA,GAAG,EAAE,KAAK,CAAC,IAAI,EAAA;wBACjB,KAAC,CAAA,aAAA,CAAA,eAAe,IAAC,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAI,CAAA,CAC5F,EACL;AACJ,iBAAC,CAAC,CACC,CACN,CACJ,CACK,CACF;QACP,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC,IAAI,KAAiB,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,aAAA,EAAA,cAAc,EAAiB,EAAA,YAAA,CAAA;QAC9F,OAAO,KACN,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,aAAA,EAAiB,cAAc,EAAA;YAC7B,KAAK,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,IAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,CAAO,CAC5E,CACP;AACA,QAAA,KAAK,CAAC,MAAM,KACX,KAAC,CAAA,aAAA,CAAA,MAAM,EAAC,EAAA,OAAO,EAAE,MAAO,KAAK,CAAC,MAAiC,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,EAAA,EAAA,SAAA,CAExF,CACV,CACG,EACN;AACJ,CAAC;AAEY,MAAA,qBAAqB,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa;;;;"}
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Parses an HTML form and returns the result as a JavaScript object.
3
3
  * @param form The HTML form element.
4
+ * @returns Form values in key value pairs.
4
5
  */
5
6
  function parseForm(form) {
6
7
  const result = {};
@@ -21,8 +22,8 @@ function parseForm(form) {
21
22
  * Parses an HTML input element.
22
23
  * Sets the name/value pair in the result,
23
24
  * but only if the element is enabled and checked.
24
- * @param el The input element.
25
25
  * @param result The result builder.
26
+ * @param el The input element.
26
27
  */
27
28
  function parseInputElement(result, el) {
28
29
  if (el.disabled) {
@@ -1 +1 @@
1
- {"version":3,"file":"FormUtils.mjs","sources":["../../../src/Form/FormUtils.ts"],"sourcesContent":["/**\n * Parses an HTML form and returns the result as a JavaScript object.\n * @param form The HTML form element.\n */\nexport function parseForm(form: HTMLFormElement): Record<string, string> {\n const result: Record<string, string> = {};\n\n for (const element of Array.from(form.elements)) {\n if (element instanceof HTMLInputElement) {\n parseInputElement(result, element);\n } else if (element instanceof HTMLTextAreaElement) {\n result[element.name] = element.value;\n } else if (element instanceof HTMLSelectElement) {\n parseSelectElement(result, element);\n }\n }\n\n return result;\n}\n\n/**\n * Parses an HTML input element.\n * Sets the name/value pair in the result,\n * but only if the element is enabled and checked.\n * @param el The input element.\n * @param result The result builder.\n */\nfunction parseInputElement(result: Record<string, string>, el: HTMLInputElement): void {\n if (el.disabled) {\n // Ignore disabled elements\n return;\n }\n\n if ((el.type === 'checkbox' || el.type === 'radio') && !el.checked) {\n // Ignore unchecked radio or checkbox elements\n return;\n }\n\n result[el.name] = el.value;\n}\n\n/**\n * Parses an HTML select element.\n * Sets the name/value pair if one is selected.\n * @param result The result builder.\n * @param el The select element.\n */\nfunction parseSelectElement(result: Record<string, string>, el: HTMLSelectElement): void {\n result[el.name] = el.value;\n}\n"],"names":[],"mappings":"AAAA;;;AAGG;AACG,SAAU,SAAS,CAAC,IAAqB,EAAA;IAC7C,MAAM,MAAM,GAA2B,EAAE,CAAC;IAE1C,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;QAC/C,IAAI,OAAO,YAAY,gBAAgB,EAAE;AACvC,YAAA,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACpC,SAAA;aAAM,IAAI,OAAO,YAAY,mBAAmB,EAAE;YACjD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;AACtC,SAAA;aAAM,IAAI,OAAO,YAAY,iBAAiB,EAAE;AAC/C,YAAA,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACrC,SAAA;AACF,KAAA;AAED,IAAA,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;AAMG;AACH,SAAS,iBAAiB,CAAC,MAA8B,EAAE,EAAoB,EAAA;IAC7E,IAAI,EAAE,CAAC,QAAQ,EAAE;;QAEf,OAAO;AACR,KAAA;AAED,IAAA,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,UAAU,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE;;QAElE,OAAO;AACR,KAAA;IAED,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;AAC7B,CAAC;AAED;;;;;AAKG;AACH,SAAS,kBAAkB,CAAC,MAA8B,EAAE,EAAqB,EAAA;IAC/E,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;AAC7B;;;;"}
1
+ {"version":3,"file":"FormUtils.mjs","sources":["../../../src/Form/FormUtils.ts"],"sourcesContent":["/**\n * Parses an HTML form and returns the result as a JavaScript object.\n * @param form The HTML form element.\n * @returns Form values in key value pairs.\n */\nexport function parseForm(form: HTMLFormElement): Record<string, string> {\n const result: Record<string, string> = {};\n\n for (const element of Array.from(form.elements)) {\n if (element instanceof HTMLInputElement) {\n parseInputElement(result, element);\n } else if (element instanceof HTMLTextAreaElement) {\n result[element.name] = element.value;\n } else if (element instanceof HTMLSelectElement) {\n parseSelectElement(result, element);\n }\n }\n\n return result;\n}\n\n/**\n * Parses an HTML input element.\n * Sets the name/value pair in the result,\n * but only if the element is enabled and checked.\n * @param result The result builder.\n * @param el The input element.\n */\nfunction parseInputElement(result: Record<string, string>, el: HTMLInputElement): void {\n if (el.disabled) {\n // Ignore disabled elements\n return;\n }\n\n if ((el.type === 'checkbox' || el.type === 'radio') && !el.checked) {\n // Ignore unchecked radio or checkbox elements\n return;\n }\n\n result[el.name] = el.value;\n}\n\n/**\n * Parses an HTML select element.\n * Sets the name/value pair if one is selected.\n * @param result The result builder.\n * @param el The select element.\n */\nfunction parseSelectElement(result: Record<string, string>, el: HTMLSelectElement): void {\n result[el.name] = el.value;\n}\n"],"names":[],"mappings":"AAAA;;;;AAIG;AACG,SAAU,SAAS,CAAC,IAAqB,EAAA;IAC7C,MAAM,MAAM,GAA2B,EAAE,CAAC;IAE1C,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;QAC/C,IAAI,OAAO,YAAY,gBAAgB,EAAE;AACvC,YAAA,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACpC,SAAA;aAAM,IAAI,OAAO,YAAY,mBAAmB,EAAE;YACjD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;AACtC,SAAA;aAAM,IAAI,OAAO,YAAY,iBAAiB,EAAE;AAC/C,YAAA,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACrC,SAAA;AACF,KAAA;AAED,IAAA,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;AAMG;AACH,SAAS,iBAAiB,CAAC,MAA8B,EAAE,EAAoB,EAAA;IAC7E,IAAI,EAAE,CAAC,QAAQ,EAAE;;QAEf,OAAO;AACR,KAAA;AAED,IAAA,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,UAAU,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE;;QAElE,OAAO;AACR,KAAA;IAED,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;AAC7B,CAAC;AAED;;;;;AAKG;AACH,SAAS,kBAAkB,CAAC,MAA8B,EAAE,EAAqB,EAAA;IAC/E,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;AAC7B;;;;"}
@@ -1,3 +1,4 @@
1
+ import { showNotification } from '@mantine/notifications';
1
2
  import React, { createContext, useState, useEffect, useContext } from 'react';
2
3
 
3
4
  const reactContext = createContext(undefined);
@@ -7,6 +8,8 @@ const reactContext = createContext(undefined);
7
8
  * Medplum context includes:
8
9
  * 1) medplum - Medplum client library
9
10
  * 2) profile - The current user profile (if signed in)
11
+ * @param props The MedplumProvider React props.
12
+ * @returns The MedplumProvider React node.
10
13
  */
11
14
  function MedplumProvider(props) {
12
15
  const medplum = props.medplum;
@@ -25,6 +28,13 @@ function MedplumProvider(props) {
25
28
  medplum.addEventListener('change', eventListener);
26
29
  return () => medplum.removeEventListeneer('change', eventListener);
27
30
  }, [medplum, state]);
31
+ useEffect(() => {
32
+ function eventListener() {
33
+ showNotification({ color: 'red', message: 'No connection to server', autoClose: false });
34
+ }
35
+ medplum.addEventListener('offline', eventListener);
36
+ return () => medplum.removeEventListeneer('offline', eventListener);
37
+ }, [medplum]);
28
38
  const medplumContext = {
29
39
  ...state,
30
40
  medplum,
@@ -34,6 +44,7 @@ function MedplumProvider(props) {
34
44
  }
35
45
  /**
36
46
  * Returns the MedplumContext instance.
47
+ * @returns The MedplumContext instance.
37
48
  */
38
49
  function useMedplumContext() {
39
50
  return useContext(reactContext);
@@ -41,6 +52,7 @@ function useMedplumContext() {
41
52
  /**
42
53
  * Returns the MedplumClient instance.
43
54
  * This is a shortcut for useMedplumContext().medplum.
55
+ * @returns The MedplumClient instance.
44
56
  */
45
57
  function useMedplum() {
46
58
  return useMedplumContext().medplum;
@@ -1 +1 @@
1
- {"version":3,"file":"MedplumProvider.mjs","sources":["../../../src/MedplumProvider/MedplumProvider.tsx"],"sourcesContent":["import { MedplumClient, ProfileResource } from '@medplum/core';\nimport React, { createContext, useContext, useEffect, useState } from 'react';\n\nconst reactContext = createContext(undefined as MedplumContext | undefined);\n\nexport type MepdlumNavigateFunction = (path: string) => void;\n\nexport interface MedplumProviderProps {\n medplum: MedplumClient;\n navigate?: MepdlumNavigateFunction;\n children: React.ReactNode;\n}\n\nexport interface MedplumContext {\n medplum: MedplumClient;\n navigate: MepdlumNavigateFunction;\n profile?: ProfileResource;\n loading: boolean;\n}\n\n/**\n * The MedplumProvider component provides Medplum context state.\n *\n * Medplum context includes:\n * 1) medplum - Medplum client library\n * 2) profile - The current user profile (if signed in)\n */\nexport function MedplumProvider(props: MedplumProviderProps): JSX.Element {\n const medplum = props.medplum;\n const navigate = props.navigate || defaultNavigate;\n\n const [state, setState] = useState({\n profile: medplum.getProfile(),\n loading: false,\n });\n\n useEffect(() => {\n function eventListener(): void {\n setState({\n ...state,\n profile: medplum.getProfile(),\n });\n }\n\n medplum.addEventListener('change', eventListener);\n return () => medplum.removeEventListeneer('change', eventListener);\n }, [medplum, state]);\n\n const medplumContext = {\n ...state,\n medplum,\n navigate,\n };\n\n return <reactContext.Provider value={medplumContext}>{props.children}</reactContext.Provider>;\n}\n\n/**\n * Returns the MedplumContext instance.\n */\nexport function useMedplumContext(): MedplumContext {\n return useContext(reactContext) as MedplumContext;\n}\n\n/**\n * Returns the MedplumClient instance.\n * This is a shortcut for useMedplumContext().medplum.\n */\nexport function useMedplum(): MedplumClient {\n return useMedplumContext().medplum;\n}\n\n/**\n * Returns the Medplum navigate function.\n * @returns The Medplum navigate function.\n */\nexport function useMedplumNavigate(): MepdlumNavigateFunction {\n return useMedplumContext().navigate;\n}\n\n/**\n * Returns the current Medplum user profile (if signed in).\n * This is a shortcut for useMedplumContext().profile.\n * @returns The current user profile.\n */\nexport function useMedplumProfile(): ProfileResource | undefined {\n return useMedplumContext().profile;\n}\n\n/**\n * The default \"navigate\" function which simply uses window.location.href.\n * @param path The path to navigate to.\n */\nfunction defaultNavigate(path: string): void {\n window.location.assign(path);\n}\n"],"names":[],"mappings":";;AAGA,MAAM,YAAY,GAAG,aAAa,CAAC,SAAuC,CAAC,CAAC;AAiB5E;;;;;;AAMG;AACG,SAAU,eAAe,CAAC,KAA2B,EAAA;AACzD,IAAA,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;AAC9B,IAAA,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,eAAe,CAAC;AAEnD,IAAA,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC;AACjC,QAAA,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE;AAC7B,QAAA,OAAO,EAAE,KAAK;AACf,KAAA,CAAC,CAAC;IAEH,SAAS,CAAC,MAAK;AACb,QAAA,SAAS,aAAa,GAAA;AACpB,YAAA,QAAQ,CAAC;AACP,gBAAA,GAAG,KAAK;AACR,gBAAA,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE;AAC9B,aAAA,CAAC,CAAC;SACJ;AAED,QAAA,OAAO,CAAC,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QAClD,OAAO,MAAM,OAAO,CAAC,oBAAoB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;AACrE,KAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;AAErB,IAAA,MAAM,cAAc,GAAG;AACrB,QAAA,GAAG,KAAK;QACR,OAAO;QACP,QAAQ;KACT,CAAC;AAEF,IAAA,OAAO,KAAC,CAAA,aAAA,CAAA,YAAY,CAAC,QAAQ,EAAC,EAAA,KAAK,EAAE,cAAc,EAAG,EAAA,KAAK,CAAC,QAAQ,CAAyB,CAAC;AAChG,CAAC;AAED;;AAEG;SACa,iBAAiB,GAAA;AAC/B,IAAA,OAAO,UAAU,CAAC,YAAY,CAAmB,CAAC;AACpD,CAAC;AAED;;;AAGG;SACa,UAAU,GAAA;AACxB,IAAA,OAAO,iBAAiB,EAAE,CAAC,OAAO,CAAC;AACrC,CAAC;AAED;;;AAGG;SACa,kBAAkB,GAAA;AAChC,IAAA,OAAO,iBAAiB,EAAE,CAAC,QAAQ,CAAC;AACtC,CAAC;AAED;;;;AAIG;SACa,iBAAiB,GAAA;AAC/B,IAAA,OAAO,iBAAiB,EAAE,CAAC,OAAO,CAAC;AACrC,CAAC;AAED;;;AAGG;AACH,SAAS,eAAe,CAAC,IAAY,EAAA;AACnC,IAAA,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC/B;;;;"}
1
+ {"version":3,"file":"MedplumProvider.mjs","sources":["../../../src/MedplumProvider/MedplumProvider.tsx"],"sourcesContent":["import { MedplumClient, ProfileResource } from '@medplum/core';\nimport { showNotification } from '@mantine/notifications';\nimport React, { createContext, useContext, useEffect, useState } from 'react';\n\nconst reactContext = createContext(undefined as MedplumContext | undefined);\n\nexport type MepdlumNavigateFunction = (path: string) => void;\n\nexport interface MedplumProviderProps {\n medplum: MedplumClient;\n navigate?: MepdlumNavigateFunction;\n children: React.ReactNode;\n}\n\nexport interface MedplumContext {\n medplum: MedplumClient;\n navigate: MepdlumNavigateFunction;\n profile?: ProfileResource;\n loading: boolean;\n}\n\n/**\n * The MedplumProvider component provides Medplum context state.\n *\n * Medplum context includes:\n * 1) medplum - Medplum client library\n * 2) profile - The current user profile (if signed in)\n * @param props The MedplumProvider React props.\n * @returns The MedplumProvider React node.\n */\nexport function MedplumProvider(props: MedplumProviderProps): JSX.Element {\n const medplum = props.medplum;\n const navigate = props.navigate || defaultNavigate;\n\n const [state, setState] = useState({\n profile: medplum.getProfile(),\n loading: false,\n });\n\n useEffect(() => {\n function eventListener(): void {\n setState({\n ...state,\n profile: medplum.getProfile(),\n });\n }\n\n medplum.addEventListener('change', eventListener);\n return () => medplum.removeEventListeneer('change', eventListener);\n }, [medplum, state]);\n\n useEffect(() => {\n function eventListener(): void {\n showNotification({ color: 'red', message: 'No connection to server', autoClose: false });\n }\n medplum.addEventListener('offline', eventListener);\n return () => medplum.removeEventListeneer('offline', eventListener);\n }, [medplum]);\n\n const medplumContext = {\n ...state,\n medplum,\n navigate,\n };\n\n return <reactContext.Provider value={medplumContext}>{props.children}</reactContext.Provider>;\n}\n\n/**\n * Returns the MedplumContext instance.\n * @returns The MedplumContext instance.\n */\nexport function useMedplumContext(): MedplumContext {\n return useContext(reactContext) as MedplumContext;\n}\n\n/**\n * Returns the MedplumClient instance.\n * This is a shortcut for useMedplumContext().medplum.\n * @returns The MedplumClient instance.\n */\nexport function useMedplum(): MedplumClient {\n return useMedplumContext().medplum;\n}\n\n/**\n * Returns the Medplum navigate function.\n * @returns The Medplum navigate function.\n */\nexport function useMedplumNavigate(): MepdlumNavigateFunction {\n return useMedplumContext().navigate;\n}\n\n/**\n * Returns the current Medplum user profile (if signed in).\n * This is a shortcut for useMedplumContext().profile.\n * @returns The current user profile.\n */\nexport function useMedplumProfile(): ProfileResource | undefined {\n return useMedplumContext().profile;\n}\n\n/**\n * The default \"navigate\" function which simply uses window.location.href.\n * @param path The path to navigate to.\n */\nfunction defaultNavigate(path: string): void {\n window.location.assign(path);\n}\n"],"names":[],"mappings":";;;AAIA,MAAM,YAAY,GAAG,aAAa,CAAC,SAAuC,CAAC,CAAC;AAiB5E;;;;;;;;AAQG;AACG,SAAU,eAAe,CAAC,KAA2B,EAAA;AACzD,IAAA,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;AAC9B,IAAA,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,eAAe,CAAC;AAEnD,IAAA,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC;AACjC,QAAA,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE;AAC7B,QAAA,OAAO,EAAE,KAAK;AACf,KAAA,CAAC,CAAC;IAEH,SAAS,CAAC,MAAK;AACb,QAAA,SAAS,aAAa,GAAA;AACpB,YAAA,QAAQ,CAAC;AACP,gBAAA,GAAG,KAAK;AACR,gBAAA,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE;AAC9B,aAAA,CAAC,CAAC;SACJ;AAED,QAAA,OAAO,CAAC,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QAClD,OAAO,MAAM,OAAO,CAAC,oBAAoB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;AACrE,KAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAErB,SAAS,CAAC,MAAK;AACb,QAAA,SAAS,aAAa,GAAA;AACpB,YAAA,gBAAgB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,yBAAyB,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;SAC1F;AACD,QAAA,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACnD,OAAO,MAAM,OAAO,CAAC,oBAAoB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AACtE,KAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;AAEd,IAAA,MAAM,cAAc,GAAG;AACrB,QAAA,GAAG,KAAK;QACR,OAAO;QACP,QAAQ;KACT,CAAC;AAEF,IAAA,OAAO,KAAC,CAAA,aAAA,CAAA,YAAY,CAAC,QAAQ,EAAC,EAAA,KAAK,EAAE,cAAc,EAAG,EAAA,KAAK,CAAC,QAAQ,CAAyB,CAAC;AAChG,CAAC;AAED;;;AAGG;SACa,iBAAiB,GAAA;AAC/B,IAAA,OAAO,UAAU,CAAC,YAAY,CAAmB,CAAC;AACpD,CAAC;AAED;;;;AAIG;SACa,UAAU,GAAA;AACxB,IAAA,OAAO,iBAAiB,EAAE,CAAC,OAAO,CAAC;AACrC,CAAC;AAED;;;AAGG;SACa,kBAAkB,GAAA;AAChC,IAAA,OAAO,iBAAiB,EAAE,CAAC,QAAQ,CAAC;AACtC,CAAC;AAED;;;;AAIG;SACa,iBAAiB,GAAA;AAC/B,IAAA,OAAO,iBAAiB,EAAE,CAAC,OAAO,CAAC;AACrC,CAAC;AAED;;;AAGG;AACH,SAAS,eAAe,CAAC,IAAY,EAAA;AACnC,IAAA,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC/B;;;;"}
@@ -222,7 +222,8 @@ let nextId = 1;
222
222
  * React needs unique IDs for components for rendering performance.
223
223
  * All of the important components in the questionnaire builder have id properties for this:
224
224
  * Questionnaire, QuestionnaireItem, and QuestionnaireItemAnswerOption.
225
- * @return A unique key.
225
+ * @param existing Optional existing id which will update nextId.
226
+ * @returns A unique key.
226
227
  */
227
228
  function generateId(existing) {
228
229
  if (existing) {