synapse-react-client 4.0.36 → 4.0.37

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 (316) hide show
  1. package/dist/SWC.index.js +1 -1
  2. package/dist/aridhia-queries/index.js +11 -11
  3. package/dist/aridhia-queries/useAridhiaQuery.d.ts +1 -0
  4. package/dist/aridhia-queries/useAridhiaQuery.d.ts.map +1 -1
  5. package/dist/aridhia-queries/useAridhiaQuery.js +24 -17
  6. package/dist/aridhia-queries/useAridhiaQuery.js.map +1 -1
  7. package/dist/assets/icons/emailWithNotification.js +15 -15
  8. package/dist/assets/icons/emailWithNotification.js.map +1 -1
  9. package/dist/assets/icons/emailWithNotification.svg +13 -13
  10. package/dist/components/AccessRequirementList/AccessRequirementList.d.ts.map +1 -1
  11. package/dist/components/AccessRequirementList/AccessRequirementList.js +124 -117
  12. package/dist/components/AccessRequirementList/AccessRequirementList.js.map +1 -1
  13. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/DataAccessRequestAccessorsFilesForm/DataAccessRequestAccessorsFilesForm.d.ts.map +1 -1
  14. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/DataAccessRequestAccessorsFilesForm/DataAccessRequestAccessorsFilesForm.js +1 -1
  15. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/DataAccessRequestAccessorsFilesForm/DataAccessRequestAccessorsFilesForm.js.map +1 -1
  16. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.d.ts.map +1 -1
  17. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.js +1 -1
  18. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.js.map +1 -1
  19. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.d.ts.map +1 -1
  20. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.js +33 -32
  21. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.js.map +1 -1
  22. package/dist/components/Aridhia/AridhiaAccessStatus.d.ts.map +1 -1
  23. package/dist/components/Aridhia/AridhiaAccessStatus.js +39 -18
  24. package/dist/components/Aridhia/AridhiaAccessStatus.js.map +1 -1
  25. package/dist/components/Aridhia/DarWizard/AridhiaDarWizard.d.ts +32 -0
  26. package/dist/components/Aridhia/DarWizard/AridhiaDarWizard.d.ts.map +1 -0
  27. package/dist/components/Aridhia/DarWizard/AridhiaDarWizard.js +285 -0
  28. package/dist/components/Aridhia/DarWizard/AridhiaDarWizard.js.map +1 -0
  29. package/dist/components/Aridhia/DarWizard/DarAboutStep.d.ts +4 -0
  30. package/dist/components/Aridhia/DarWizard/DarAboutStep.d.ts.map +1 -0
  31. package/dist/components/Aridhia/DarWizard/DarAboutStep.js +17 -0
  32. package/dist/components/Aridhia/DarWizard/DarAboutStep.js.map +1 -0
  33. package/dist/components/Aridhia/DarWizard/DarCompleteStep.d.ts +27 -0
  34. package/dist/components/Aridhia/DarWizard/DarCompleteStep.d.ts.map +1 -0
  35. package/dist/components/Aridhia/DarWizard/DarCompleteStep.js +96 -0
  36. package/dist/components/Aridhia/DarWizard/DarCompleteStep.js.map +1 -0
  37. package/dist/components/Aridhia/DarWizard/DarDestinationStep.d.ts +28 -0
  38. package/dist/components/Aridhia/DarWizard/DarDestinationStep.d.ts.map +1 -0
  39. package/dist/components/Aridhia/DarWizard/DarDestinationStep.js +128 -0
  40. package/dist/components/Aridhia/DarWizard/DarDestinationStep.js.map +1 -0
  41. package/dist/components/Aridhia/DarWizard/DarProjectStep.d.ts +4 -0
  42. package/dist/components/Aridhia/DarWizard/DarProjectStep.d.ts.map +1 -0
  43. package/dist/components/Aridhia/DarWizard/DarProjectStep.js +17 -0
  44. package/dist/components/Aridhia/DarWizard/DarProjectStep.js.map +1 -0
  45. package/dist/components/Aridhia/DarWizard/DarSectionForm.d.ts +13 -0
  46. package/dist/components/Aridhia/DarWizard/DarSectionForm.d.ts.map +1 -0
  47. package/dist/components/Aridhia/DarWizard/DarSectionForm.js +23 -0
  48. package/dist/components/Aridhia/DarWizard/DarSectionForm.js.map +1 -0
  49. package/dist/components/Aridhia/DarWizard/DarSubmittedPanel.d.ts +11 -0
  50. package/dist/components/Aridhia/DarWizard/DarSubmittedPanel.d.ts.map +1 -0
  51. package/dist/components/Aridhia/DarWizard/DarSubmittedPanel.js +47 -0
  52. package/dist/components/Aridhia/DarWizard/DarSubmittedPanel.js.map +1 -0
  53. package/dist/components/Aridhia/DarWizard/DarWorkspaceStep.d.ts +16 -0
  54. package/dist/components/Aridhia/DarWizard/DarWorkspaceStep.d.ts.map +1 -0
  55. package/dist/components/Aridhia/DarWizard/DarWorkspaceStep.js +33 -0
  56. package/dist/components/Aridhia/DarWizard/DarWorkspaceStep.js.map +1 -0
  57. package/dist/components/Aridhia/DarWizard/useDarDraft.d.ts +32 -0
  58. package/dist/components/Aridhia/DarWizard/useDarDraft.d.ts.map +1 -0
  59. package/dist/components/Aridhia/DarWizard/useDarDraft.js +44 -0
  60. package/dist/components/Aridhia/DarWizard/useDarDraft.js.map +1 -0
  61. package/dist/components/Aridhia/FairLabelField.d.ts +8 -0
  62. package/dist/components/Aridhia/FairLabelField.d.ts.map +1 -0
  63. package/dist/components/Aridhia/FairLabelField.js +13 -0
  64. package/dist/components/Aridhia/FairLabelField.js.map +1 -0
  65. package/dist/components/Aridhia/fairFormToRjsf.d.ts +36 -0
  66. package/dist/components/Aridhia/fairFormToRjsf.d.ts.map +1 -0
  67. package/dist/components/Aridhia/fairFormToRjsf.js +137 -0
  68. package/dist/components/Aridhia/fairFormToRjsf.js.map +1 -0
  69. package/dist/components/CannedRejectionDialog/CannedRejectionDialog.d.ts.map +1 -1
  70. package/dist/components/CannedRejectionDialog/CannedRejectionDialog.js +2 -2
  71. package/dist/components/CannedRejectionDialog/CannedRejectionDialog.js.map +1 -1
  72. package/dist/components/CardContainer/CardContainer.d.ts.map +1 -1
  73. package/dist/components/CardContainer/CardContainer.js +1 -1
  74. package/dist/components/CardContainer/CardContainer.js.map +1 -1
  75. package/dist/components/ChallengeSubmission/ChallengeSubmission.d.ts.map +1 -1
  76. package/dist/components/ChallengeSubmission/ChallengeSubmission.js +2 -2
  77. package/dist/components/ChallengeSubmission/ChallengeSubmission.js.map +1 -1
  78. package/dist/components/ChallengeTeamWizard/ChallengeTeamWizard.d.ts.map +1 -1
  79. package/dist/components/ChallengeTeamWizard/ChallengeTeamWizard.js +3 -3
  80. package/dist/components/ChallengeTeamWizard/ChallengeTeamWizard.js.map +1 -1
  81. package/dist/components/ChallengeTeamWizard/CreateChallengeTeam.js +1 -1
  82. package/dist/components/ChallengeTeamWizard/CreateChallengeTeam.js.map +1 -1
  83. package/dist/components/CreateTableViewWizard/CreateTableViewWizard.js +2 -2
  84. package/dist/components/CreateTableViewWizard/CreateTableViewWizard.js.map +1 -1
  85. package/dist/components/DataGrid/SynapseGrid.d.ts.map +1 -1
  86. package/dist/components/DataGrid/SynapseGrid.js +121 -117
  87. package/dist/components/DataGrid/SynapseGrid.js.map +1 -1
  88. package/dist/components/DataGrid/columns/DateTimeColumn.d.ts +21 -1
  89. package/dist/components/DataGrid/columns/DateTimeColumn.d.ts.map +1 -1
  90. package/dist/components/DataGrid/columns/DateTimeColumn.js +73 -29
  91. package/dist/components/DataGrid/columns/DateTimeColumn.js.map +1 -1
  92. package/dist/components/DataGrid/columns/NumberColumn.d.ts +31 -0
  93. package/dist/components/DataGrid/columns/NumberColumn.d.ts.map +1 -0
  94. package/dist/components/DataGrid/columns/NumberColumn.js +27 -0
  95. package/dist/components/DataGrid/columns/NumberColumn.js.map +1 -0
  96. package/dist/components/DataGrid/components/ReorderColumnsButton.d.ts +1 -0
  97. package/dist/components/DataGrid/components/ReorderColumnsButton.d.ts.map +1 -1
  98. package/dist/components/DataGrid/components/ReorderColumnsButton.js +17 -16
  99. package/dist/components/DataGrid/components/ReorderColumnsButton.js.map +1 -1
  100. package/dist/components/DataGrid/components/ReorderColumnsDialog.d.ts +2 -0
  101. package/dist/components/DataGrid/components/ReorderColumnsDialog.d.ts.map +1 -1
  102. package/dist/components/DataGrid/components/ReorderColumnsDialog.js +104 -65
  103. package/dist/components/DataGrid/components/ReorderColumnsDialog.js.map +1 -1
  104. package/dist/components/DataGrid/useDataGridWebsocket.js +1 -1
  105. package/dist/components/DataGrid/useDataGridWebsocket.js.map +1 -1
  106. package/dist/components/DataGrid/utils/columnFactory.d.ts.map +1 -1
  107. package/dist/components/DataGrid/utils/columnFactory.js +55 -51
  108. package/dist/components/DataGrid/utils/columnFactory.js.map +1 -1
  109. package/dist/components/DataGrid/utils/computeDefaultColumnOrder.d.ts.map +1 -1
  110. package/dist/components/DataGrid/utils/computeDefaultColumnOrder.js +9 -8
  111. package/dist/components/DataGrid/utils/computeDefaultColumnOrder.js.map +1 -1
  112. package/dist/components/DatasetHosting/DatasetDownloadButton.d.ts +10 -5
  113. package/dist/components/DatasetHosting/DatasetDownloadButton.d.ts.map +1 -1
  114. package/dist/components/DatasetHosting/DatasetDownloadButton.js +66 -74
  115. package/dist/components/DatasetHosting/DatasetDownloadButton.js.map +1 -1
  116. package/dist/components/DatasetHosting/DatasetHosting.d.ts +6 -0
  117. package/dist/components/DatasetHosting/DatasetHosting.d.ts.map +1 -1
  118. package/dist/components/DatasetHosting/DatasetHosting.js +6 -0
  119. package/dist/components/DatasetHosting/DatasetHosting.js.map +1 -1
  120. package/dist/components/DatasetHosting/DatasetHostingAdornment.d.ts +15 -0
  121. package/dist/components/DatasetHosting/DatasetHostingAdornment.d.ts.map +1 -0
  122. package/dist/components/DatasetHosting/DatasetHostingAdornment.js +23 -0
  123. package/dist/components/DatasetHosting/DatasetHostingAdornment.js.map +1 -0
  124. package/dist/components/DatasetHosting/index.d.ts +2 -0
  125. package/dist/components/DatasetHosting/index.d.ts.map +1 -1
  126. package/dist/components/DatasetHosting/index.js +2 -1
  127. package/dist/components/DownloadCart/DownloadCartPage.d.ts.map +1 -1
  128. package/dist/components/DownloadCart/DownloadCartPage.js +1 -1
  129. package/dist/components/DownloadCart/DownloadCartPage.js.map +1 -1
  130. package/dist/components/DropdownSelect/DropdownSelect.d.ts.map +1 -1
  131. package/dist/components/DropdownSelect/DropdownSelect.js +1 -1
  132. package/dist/components/DropdownSelect/DropdownSelect.js.map +1 -1
  133. package/dist/components/EntityFinder/tree/EntityTree.d.ts.map +1 -1
  134. package/dist/components/EntityFinder/tree/EntityTree.js +1 -1
  135. package/dist/components/EntityFinder/tree/EntityTree.js.map +1 -1
  136. package/dist/components/EntityFinder/tree/VirtualizedTree.js +1 -1
  137. package/dist/components/EntityFinder/tree/VirtualizedTree.js.map +1 -1
  138. package/dist/components/FeaturedDataTabs/FacetPlotsCard.js +1 -1
  139. package/dist/components/FeaturedDataTabs/FacetPlotsCard.js.map +1 -1
  140. package/dist/components/GenericCard/TableRowGenericCard.d.ts.map +1 -1
  141. package/dist/components/GenericCard/TableRowGenericCard.js +176 -170
  142. package/dist/components/GenericCard/TableRowGenericCard.js.map +1 -1
  143. package/dist/components/JsonSchemaForm/CustomFormContext.d.ts +9 -0
  144. package/dist/components/JsonSchemaForm/CustomFormContext.d.ts.map +1 -1
  145. package/dist/components/JsonSchemaForm/JsonSchemaForm.d.ts.map +1 -1
  146. package/dist/components/JsonSchemaForm/JsonSchemaForm.js +2 -1
  147. package/dist/components/JsonSchemaForm/JsonSchemaForm.js.map +1 -1
  148. package/dist/components/JsonSchemaForm/templates/ArrayFieldTemplate.d.ts.map +1 -1
  149. package/dist/components/JsonSchemaForm/templates/ArrayFieldTemplate.js +1 -1
  150. package/dist/components/JsonSchemaForm/templates/ArrayFieldTemplate.js.map +1 -1
  151. package/dist/components/JsonSchemaForm/templates/FieldTemplate.d.ts.map +1 -1
  152. package/dist/components/JsonSchemaForm/templates/FieldTemplate.js +6 -6
  153. package/dist/components/JsonSchemaForm/templates/FieldTemplate.js.map +1 -1
  154. package/dist/components/JsonSchemaForm/widgets/DateWidget.d.ts +9 -0
  155. package/dist/components/JsonSchemaForm/widgets/DateWidget.d.ts.map +1 -0
  156. package/dist/components/JsonSchemaForm/widgets/DateWidget.js +23 -0
  157. package/dist/components/JsonSchemaForm/widgets/DateWidget.js.map +1 -0
  158. package/dist/components/MailchimpSubscribeSection/MailchimpSubscribeSection.css +1 -1
  159. package/dist/components/MailchimpSubscribeSection/MailchimpSubscribeSection.d.ts +3 -2
  160. package/dist/components/MailchimpSubscribeSection/MailchimpSubscribeSection.d.ts.map +1 -1
  161. package/dist/components/MailchimpSubscribeSection/MailchimpSubscribeSection.js +10 -12
  162. package/dist/components/MailchimpSubscribeSection/MailchimpSubscribeSection.js.map +1 -1
  163. package/dist/components/MailchimpSubscribeSection/MailchimpSubscribeSection.module.js +8 -7
  164. package/dist/components/MailchimpSubscribeSection/MailchimpSubscribeSection.module.js.map +1 -1
  165. package/dist/components/MailchimpSubscribeSection/MailchimpSubscribeSection.module.scss +12 -2
  166. package/dist/components/Markdown/MarkdownEditor.js +1 -1
  167. package/dist/components/Markdown/MarkdownEditor.js.map +1 -1
  168. package/dist/components/ProvenanceGraph/ProvenanceGraph.d.ts.map +1 -1
  169. package/dist/components/ProvenanceGraph/ProvenanceGraph.js +46 -46
  170. package/dist/components/ProvenanceGraph/ProvenanceGraph.js.map +1 -1
  171. package/dist/components/QueryBuilder/queryBuilderTranslation.js +1 -1
  172. package/dist/components/QueryBuilder/queryBuilderTranslation.js.map +1 -1
  173. package/dist/components/QueryWrapperPlotNav/LastUpdatedOn.js +1 -1
  174. package/dist/components/QueryWrapperPlotNav/LastUpdatedOn.js.map +1 -1
  175. package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.d.ts +1 -1
  176. package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.d.ts.map +1 -1
  177. package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.js +27 -25
  178. package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.js.map +1 -1
  179. package/dist/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.d.ts +0 -9
  180. package/dist/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.d.ts.map +1 -1
  181. package/dist/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.js +23 -31
  182. package/dist/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.js.map +1 -1
  183. package/dist/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.d.ts.map +1 -1
  184. package/dist/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.js +5 -4
  185. package/dist/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.js.map +1 -1
  186. package/dist/components/SchemaDrivenAnnotationEditor/field/SynapseAnnotationsRJSFObjectField.d.ts.map +1 -1
  187. package/dist/components/SchemaDrivenAnnotationEditor/field/SynapseAnnotationsRJSFObjectField.js +6 -5
  188. package/dist/components/SchemaDrivenAnnotationEditor/field/SynapseAnnotationsRJSFObjectField.js.map +1 -1
  189. package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.d.ts +2 -0
  190. package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.d.ts.map +1 -1
  191. package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.js +18 -13
  192. package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.js.map +1 -1
  193. package/dist/components/StatisticsPlot.d.ts.map +1 -1
  194. package/dist/components/StatisticsPlot.js +1 -1
  195. package/dist/components/StatisticsPlot.js.map +1 -1
  196. package/dist/components/SynapseForm/SynapseFormWrapper.js +1 -1
  197. package/dist/components/SynapseForm/SynapseFormWrapper.js.map +1 -1
  198. package/dist/components/SynapseHomepageV2/HotdropsAndNews.css +1 -1
  199. package/dist/components/SynapseHomepageV2/HotdropsAndNews.module.js +9 -9
  200. package/dist/components/SynapseHomepageV2/HotdropsAndNews.module.js.map +1 -1
  201. package/dist/components/SynapseHomepageV2/HotdropsAndNews.module.scss +14 -0
  202. package/dist/components/SynapseHomepageV2/SynapseHomepageChatSearch.js +1 -1
  203. package/dist/components/SynapseHomepageV2/SynapseHomepageChatSearch.js.map +1 -1
  204. package/dist/components/SynapseSearchPageResults/SynapseSearchPage.js +1 -1
  205. package/dist/components/SynapseSearchPageResults/SynapseSearchPage.js.map +1 -1
  206. package/dist/components/SynapseTable/SynapseTableRenderers.d.ts.map +1 -1
  207. package/dist/components/SynapseTable/SynapseTableRenderers.js +1 -1
  208. package/dist/components/SynapseTable/SynapseTableRenderers.js.map +1 -1
  209. package/dist/components/SynapseTable/TopLevelControls/TopLevelControls.d.ts +4 -0
  210. package/dist/components/SynapseTable/TopLevelControls/TopLevelControls.d.ts.map +1 -1
  211. package/dist/components/SynapseTable/TopLevelControls/TopLevelControls.js +71 -69
  212. package/dist/components/SynapseTable/TopLevelControls/TopLevelControls.js.map +1 -1
  213. package/dist/components/SynapseTable/table-top/DownloadOptions.d.ts +4 -0
  214. package/dist/components/SynapseTable/table-top/DownloadOptions.d.ts.map +1 -1
  215. package/dist/components/SynapseTable/table-top/DownloadOptions.js +14 -13
  216. package/dist/components/SynapseTable/table-top/DownloadOptions.js.map +1 -1
  217. package/dist/components/SynapseTable/usePrefetchTableData.js +1 -1
  218. package/dist/components/SynapseTable/usePrefetchTableData.js.map +1 -1
  219. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaFormReducer.js +4 -4
  220. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaFormReducer.js.map +1 -1
  221. package/dist/components/TanStackTable/TableCellRenderer.d.ts.map +1 -1
  222. package/dist/components/TanStackTable/TableCellRenderer.js +1 -1
  223. package/dist/components/TanStackTable/TableCellRenderer.js.map +1 -1
  224. package/dist/components/TermsAndConditions/TermsAndConditions.js +1 -1
  225. package/dist/components/TermsAndConditions/TermsAndConditions.js.map +1 -1
  226. package/dist/components/Webhook/CreateWebhookModal.d.ts.map +1 -1
  227. package/dist/components/Webhook/CreateWebhookModal.js +1 -1
  228. package/dist/components/Webhook/CreateWebhookModal.js.map +1 -1
  229. package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.d.ts.map +1 -1
  230. package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.js +172 -63
  231. package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.js.map +1 -1
  232. package/dist/components/dataaccess/UserAccessRequestHistory/RouteConstants.d.ts +3 -0
  233. package/dist/components/dataaccess/UserAccessRequestHistory/RouteConstants.d.ts.map +1 -1
  234. package/dist/components/dataaccess/UserAccessRequestHistory/RouteConstants.js +2 -2
  235. package/dist/components/dataaccess/UserAccessRequestHistory/RouteConstants.js.map +1 -1
  236. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryPage.d.ts.map +1 -1
  237. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryPage.js +10 -8
  238. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryPage.js.map +1 -1
  239. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryPlace.d.ts.map +1 -1
  240. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryPlace.js +74 -43
  241. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryPlace.js.map +1 -1
  242. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryTable.d.ts.map +1 -1
  243. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryTable.js +2 -2
  244. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryTable.js.map +1 -1
  245. package/dist/components/doi/CreateOrUpdateDoiModal.js +5 -5
  246. package/dist/components/doi/CreateOrUpdateDoiModal.js.map +1 -1
  247. package/dist/components/entity/metadata/EntityModal.js +1 -1
  248. package/dist/components/entity/metadata/EntityModal.js.map +1 -1
  249. package/dist/components/entity/page/action_menu/EntityActionMenu.js +2 -2
  250. package/dist/components/entity/page/action_menu/EntityActionMenu.js.map +1 -1
  251. package/dist/components/index.js +113 -112
  252. package/dist/components/menu/DropdownMenu.d.ts.map +1 -1
  253. package/dist/components/menu/DropdownMenu.js +1 -1
  254. package/dist/components/menu/DropdownMenu.js.map +1 -1
  255. package/dist/components/row_renderers/Dataset.js +1 -1
  256. package/dist/components/row_renderers/Dataset.js.map +1 -1
  257. package/dist/components/styled/LeftRightPanel.d.ts +8 -0
  258. package/dist/components/styled/LeftRightPanel.d.ts.map +1 -1
  259. package/dist/components/styled/LeftRightPanel.js +10 -1
  260. package/dist/components/styled/LeftRightPanel.js.map +1 -1
  261. package/dist/components/widgets/facet-nav/FacetNavPanel.js +1 -1
  262. package/dist/components/widgets/facet-nav/FacetNavPanel.js.map +1 -1
  263. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.d.ts.map +1 -1
  264. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.js +1 -1
  265. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.js.map +1 -1
  266. package/dist/components/widgets/query-filter/FacetFilterControls.js +1 -1
  267. package/dist/components/widgets/query-filter/FacetFilterControls.js.map +1 -1
  268. package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.d.ts.map +1 -1
  269. package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.js +1 -1
  270. package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.js.map +1 -1
  271. package/dist/index.js +174 -173
  272. package/dist/index.js.map +1 -1
  273. package/dist/mocks/msw/handlers/aridhiaHandlers.d.ts +73 -0
  274. package/dist/mocks/msw/handlers/aridhiaHandlers.d.ts.map +1 -0
  275. package/dist/mocks/msw/handlers/aridhiaHandlers.js +119 -0
  276. package/dist/mocks/msw/handlers/aridhiaHandlers.js.map +1 -0
  277. package/dist/mocks/msw/handlers/dataAccessRequestHandlers.js +1 -1
  278. package/dist/mocks/msw/handlers/dataAccessRequestHandlers.js.map +1 -1
  279. package/dist/mocks/msw/handlers/entityHandlers.d.ts.map +1 -1
  280. package/dist/mocks/msw/handlers/entityHandlers.js +4 -4
  281. package/dist/mocks/msw/handlers/entityHandlers.js.map +1 -1
  282. package/dist/mocks/msw/handlers/userProfileHandlers.js +2 -2
  283. package/dist/mocks/msw/handlers/userProfileHandlers.js.map +1 -1
  284. package/dist/mocks/msw/handlers.d.ts.map +1 -1
  285. package/dist/mocks/msw/handlers.js +72 -69
  286. package/dist/mocks/msw/handlers.js.map +1 -1
  287. package/dist/style/components/_terms-and-conditions.scss +25 -3
  288. package/dist/style/main.css +1 -1
  289. package/dist/testutils/RjsfFormTestUtils.js +12 -0
  290. package/dist/testutils/RjsfFormTestUtils.js.map +1 -0
  291. package/dist/tsconfig.build.tsbuildinfo +1 -1
  292. package/dist/utils/featureflag/FeatureFlags.d.ts +1 -0
  293. package/dist/utils/featureflag/FeatureFlags.d.ts.map +1 -1
  294. package/dist/utils/featureflag/FeatureFlags.js +1 -1
  295. package/dist/utils/featureflag/FeatureFlags.js.map +1 -1
  296. package/dist/utils/functions/QueryFilterUtils.js +1 -1
  297. package/dist/utils/functions/QueryFilterUtils.js.map +1 -1
  298. package/dist/utils/functions/SqlFunctions.js +1 -1
  299. package/dist/utils/functions/SqlFunctions.js.map +1 -1
  300. package/dist/utils/functions/queryUtils.js +1 -1
  301. package/dist/utils/functions/queryUtils.js.map +1 -1
  302. package/dist/utils/hooks/useDetectSSOCode.d.ts.map +1 -1
  303. package/dist/utils/hooks/useDetectSSOCode.js +1 -1
  304. package/dist/utils/hooks/useDetectSSOCode.js.map +1 -1
  305. package/dist/utils/hooks/useImmutableTableQuery/useImmutableTableQuery.js +1 -1
  306. package/dist/utils/hooks/useImmutableTableQuery/useImmutableTableQuery.js.map +1 -1
  307. package/dist/utils/jsonschema/SchemaAnnotationUtils.js +1 -1
  308. package/dist/utils/jsonschema/SchemaAnnotationUtils.js.map +1 -1
  309. package/dist/utils/jsonschema/collectTopLevelProperties.d.ts +22 -0
  310. package/dist/utils/jsonschema/collectTopLevelProperties.d.ts.map +1 -0
  311. package/dist/utils/jsonschema/collectTopLevelProperties.js +31 -0
  312. package/dist/utils/jsonschema/collectTopLevelProperties.js.map +1 -0
  313. package/dist/utils/jsonschema/getSchemaPropertyInfo.d.ts.map +1 -1
  314. package/dist/utils/jsonschema/getSchemaPropertyInfo.js +1 -1
  315. package/dist/utils/jsonschema/getSchemaPropertyInfo.js.map +1 -1
  316. package/package.json +4 -4
@@ -10,8 +10,13 @@ import { omit as s } from "lodash-es";
10
10
  import { FACET_COLUMN_RANGE_REQUEST_CONCRETE_TYPE_VALUE as c, FACET_COLUMN_VALUES_REQUEST_CONCRETE_TYPE_VALUE as l, TEXT_MATCHES_QUERY_FILTER_CONCRETE_TYPE_VALUE as u } from "@sage-bionetworks/synapse-types";
11
11
  import d from "dayjs";
12
12
  //#region src/components/SearchQueryWrapper/SearchQueryUseQueryOptions.ts
13
- function f(e) {
14
- if (e) return Object.entries(e).map(([e, t]) => t === 1 ? e : `${e}^${t}`);
13
+ function f(e, t) {
14
+ if (!e) return;
15
+ let n = t && !("*" in e) ? {
16
+ ...e,
17
+ "*": 1
18
+ } : e;
19
+ return Object.entries(n).map(([e, t]) => t === 1 ? e : `${e}^${t}`);
15
20
  }
16
21
  function p(e) {
17
22
  return /"[^"]+"/u.test(e);
@@ -20,41 +25,41 @@ function m(e) {
20
25
  return t.test(e);
21
26
  }
22
27
  function h(e, t = {}) {
23
- let { queryStrategy: n = "MULTI_MATCH", fieldBoosts: r, fuzziness: i } = t, a = f(r);
28
+ let { queryStrategy: n = "MULTI_MATCH", fieldBoosts: r, includeUnlistedFields: i = !0, fuzziness: a } = t, o = f(r, i);
24
29
  if (p(e) || m(e)) return { simple_query_string: {
25
30
  query: e,
26
- ...a ? { fields: a } : {}
31
+ ...o ? { fields: o } : {}
27
32
  } };
28
33
  switch (n) {
29
34
  case "SIMPLE_QUERY_STRING": return { simple_query_string: {
30
35
  query: e,
31
- ...a ? { fields: a } : {}
36
+ ...o ? { fields: o } : {}
32
37
  } };
33
38
  case "MULTI_MATCH_BEST_FIELDS": return { multi_match: {
34
39
  query: e,
35
40
  type: "best_fields",
36
- ...a ? { fields: a } : {},
37
- ...i ? { fuzziness: i } : {}
41
+ ...o ? { fields: o } : {},
42
+ ...a ? { fuzziness: a } : {}
38
43
  } };
39
44
  case "MULTI_MATCH_CROSS_FIELDS": return { multi_match: {
40
45
  query: e,
41
46
  type: "cross_fields",
42
- ...a ? { fields: a } : {},
43
- ...i ? { fuzziness: i } : {}
47
+ ...o ? { fields: o } : {},
48
+ ...a ? { fuzziness: a } : {}
44
49
  } };
45
50
  case "PHRASE_PREFIX": return { multi_match: {
46
51
  query: e,
47
52
  type: "phrase_prefix",
48
- ...a ? { fields: a } : {}
53
+ ...o ? { fields: o } : {}
49
54
  } };
50
55
  case "BOOSTED_FUZZY": return { multi_match: {
51
56
  query: e,
52
- ...a ? { fields: a } : {},
53
- fuzziness: i ?? "AUTO"
57
+ ...o ? { fields: o } : {},
58
+ fuzziness: a ?? "AUTO"
54
59
  } };
55
60
  default: return { multi_match: {
56
61
  query: e,
57
- fuzziness: i ?? "AUTO"
62
+ fuzziness: a ?? "AUTO"
58
63
  } };
59
64
  }
60
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SearchQueryUseQueryOptions.js","names":[],"sources":["../../../src/components/SearchQueryWrapper/SearchQueryUseQueryOptions.ts"],"sourcesContent":["import * as SynapseClient from '@/synapse-client/SynapseClient'\nimport { tableQueryUseQueryDefaults } from '@/synapse-queries'\nimport { useGetEntityBundle } from '@/synapse-queries/entity/useEntityBundle'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport {\n AsynchronousJobStatus,\n ColumnModel,\n FACET_COLUMN_RANGE_REQUEST_CONCRETE_TYPE_VALUE,\n FacetColumnRangeRequest,\n FacetColumnResultRange,\n FacetColumnResultValues,\n FACET_COLUMN_VALUES_REQUEST_CONCRETE_TYPE_VALUE,\n FacetColumnValuesRequest,\n QueryBundleRequest,\n QueryResultBundle,\n RowSet,\n SortItem,\n TEXT_MATCHES_QUERY_FILTER_CONCRETE_TYPE_VALUE,\n TextMatchesQueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport type {\n Aggregation,\n DslQuery,\n SearchIndexQuery,\n SearchQueryResults,\n SearchSearchQuery,\n SynapseClientError,\n TermsAggregation,\n} from '@sage-bionetworks/synapse-client'\nimport {\n InfiniteData,\n QueryKey,\n UseInfiniteQueryOptions,\n UseSuspenseQueryOptions,\n} from '@tanstack/react-query'\nimport { omit } from 'lodash-es'\nimport dayjs from 'dayjs'\nimport { useCallback, useMemo } from 'react'\nimport { KeyFactory } from '@/synapse-queries/KeyFactory'\nimport { SYNAPSE_ENTITY_ID_TOKEN_REGEX } from '@/utils/functions/RegularExpressions'\nimport { VALUE_NOT_SET } from '@/utils/SynapseConstants'\n\n// ─── Local OpenSearch DSL types ─────────────────────────────────────────────\n// Narrow typings for the OpenSearch structures we build (query DSL, sort) and\n// parse (aggregation results). These are intentionally minimal – only the fields\n// we actually produce/consume are declared. Aggregation types use the generated\n// Aggregation/TermsAggregation types from @sage-bionetworks/synapse-client.\n\nexport type SearchQueryStrategy =\n | 'MULTI_MATCH'\n | 'MULTI_MATCH_BEST_FIELDS'\n | 'MULTI_MATCH_CROSS_FIELDS'\n | 'SIMPLE_QUERY_STRING'\n | 'PHRASE_PREFIX'\n | 'BOOSTED_FUZZY'\n\nexport type SearchQueryConfig = {\n /**\n * Which OpenSearch query clause to emit when the user has typed a search term.\n * Defaults to 'MULTI_MATCH' (all fields, fuzziness AUTO — today's behavior).\n */\n queryStrategy?: SearchQueryStrategy\n /**\n * Per-field relevance multipliers. Serialized to the OpenSearch `fields` array\n * as e.g. `[\"resourceName^5\", \"synonyms^4\"]`. When omitted, all fields are\n * queried at equal weight.\n */\n fieldBoosts?: Record<string, number>\n /**\n * Fuzziness override. Overrides the per-strategy default. Leave undefined to\n * use each strategy's natural default (AUTO for MULTI_MATCH/BOOSTED_FUZZY,\n * none for the rest).\n */\n fuzziness?: string\n}\n\ntype FilterClause =\n | { terms: Record<string, string[]> }\n | { range: Record<string, { gte?: string; lte?: string }> }\n // \"Field is not set\" — emitted when a range facet selects \"Not Assigned\".\n | { bool: { must_not: [{ exists: { field: string } }] } }\n\ntype MultiMatchClause = {\n multi_match: {\n query: string\n fuzziness?: string\n type?: 'best_fields' | 'cross_fields' | 'phrase_prefix'\n fields?: string[]\n }\n}\n\ntype SimpleQueryStringClause = {\n simple_query_string: { query: string; fields?: string[] }\n}\n\ntype BoolQuery = {\n bool: {\n must?: MultiMatchClause[] | FilterClause[]\n filter?: FilterClause[]\n }\n}\n\n// post_filter / per-facet aggregation filter: a single FilterClause, match_all, or AND of many.\ntype SelectionFilter =\n | FilterClause\n | { match_all: Record<never, never> }\n | { bool: { must: FilterClause[] } }\n\ntype SearchQueryDSL =\n | MultiMatchClause\n | SimpleQueryStringClause\n | { match_all: Record<never, never> }\n | BoolQuery\n\nfunction resolveFields(\n fieldBoosts: Record<string, number> | undefined,\n): string[] | undefined {\n if (!fieldBoosts) return undefined\n return Object.entries(fieldBoosts).map(([field, boost]) =>\n boost === 1 ? field : `${field}^${boost}`,\n )\n}\n\n/**\n * Returns true if the query text contains at least one double-quoted phrase\n * (e.g. `\"cancer genomics\"`). OpenSearch `multi_match` does not understand the\n * `\"phrase\"` operator — only `simple_query_string` does — so callers use this\n * to route quoted queries appropriately.\n */\nfunction containsQuotedPhrase(text: string): boolean {\n // Match a pair of double-quotes enclosing at least one non-quote character.\n return /\"[^\"]+\"/u.test(text)\n}\n\n/**\n * Returns true if the query text contains a Synapse entity ID token\n * (e.g. `syn12345` or `syn12345.4`). `multi_match` with fuzziness AUTO can match\n * neighbouring IDs (e.g. `syn12345` → `syn12344`), which is almost never desired\n * for identifier lookups. Callers use this to route ID-containing queries to\n * `simple_query_string`, which does not apply fuzziness.\n */\nfunction containsSynapseId(text: string): boolean {\n return SYNAPSE_ENTITY_ID_TOKEN_REGEX.test(text)\n}\n\nfunction buildQueryClause(\n queryText: string,\n config: SearchQueryConfig = {},\n): MultiMatchClause | SimpleQueryStringClause {\n const { queryStrategy = 'MULTI_MATCH', fieldBoosts, fuzziness } = config\n const fields = resolveFields(fieldBoosts)\n\n // Route to simple_query_string when the query needs exact-token treatment:\n // - a double-quoted phrase (multi_match can't honour the phrase operator), or\n // - a Synapse entity ID (fuzziness would match neighbouring IDs).\n // simple_query_string doesn't apply fuzziness by default, so both cases work.\n if (containsQuotedPhrase(queryText) || containsSynapseId(queryText)) {\n return {\n simple_query_string: {\n query: queryText,\n ...(fields ? { fields } : {}),\n },\n }\n }\n\n switch (queryStrategy) {\n case 'SIMPLE_QUERY_STRING':\n return {\n simple_query_string: {\n query: queryText,\n ...(fields ? { fields } : {}),\n },\n }\n case 'MULTI_MATCH_BEST_FIELDS':\n return {\n multi_match: {\n query: queryText,\n type: 'best_fields',\n ...(fields ? { fields } : {}),\n ...(fuzziness ? { fuzziness } : {}),\n },\n }\n case 'MULTI_MATCH_CROSS_FIELDS':\n return {\n multi_match: {\n query: queryText,\n type: 'cross_fields',\n ...(fields ? { fields } : {}),\n ...(fuzziness ? { fuzziness } : {}),\n },\n }\n case 'PHRASE_PREFIX':\n return {\n multi_match: {\n query: queryText,\n type: 'phrase_prefix',\n ...(fields ? { fields } : {}),\n },\n }\n case 'BOOSTED_FUZZY':\n return {\n multi_match: {\n query: queryText,\n ...(fields ? { fields } : {}),\n fuzziness: fuzziness ?? 'AUTO',\n },\n }\n default: // 'MULTI_MATCH'\n return {\n multi_match: {\n query: queryText,\n fuzziness: fuzziness ?? 'AUTO',\n },\n }\n }\n}\n\ntype OpenSearchBucket = { key: unknown; doc_count: number }\ntype OpenSearchTermsAgg = { buckets?: OpenSearchBucket[] }\n\n/**\n * TanStack Query options for the SearchQueryServicesApi, mirroring TableQueryUseQueryOptions\n * but parameterised over SearchIndexQuery rather than QueryBundleRequest. This eliminates\n * `as unknown as` casts inside the implementation. A single cast to TableQueryUseQueryOptions\n * is still required at the boundary where SearchQueryWrapper assigns these into QueryContextType.\n */\nexport type SearchTableQueryUseQueryOptions = {\n rowDataQueryOptions: UseSuspenseQueryOptions<\n AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n SynapseClientError,\n RowSet | undefined\n >\n rowDataInfiniteQueryOptions: UseInfiniteQueryOptions<\n AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n SynapseClientError,\n InfiniteData<AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>>,\n QueryKey,\n string | number | undefined\n >\n queryMetadataQueryOptions: UseSuspenseQueryOptions<\n AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n SynapseClientError,\n Omit<QueryResultBundle, 'queryResult'>\n >\n}\n\nexport function getNextPageParamForSearchQueryResults(\n lastPage: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n allPages: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>[],\n): number | string | undefined {\n const rowsInLastPage =\n lastPage.responseBody?.queryResult?.queryResults.rows?.length ?? 0\n const requestedPageSize = lastPage.requestBody?.searchQuery?.size\n const totalRows = lastPage.responseBody?.queryCount\n const fetchedRows = allPages.reduce(\n (acc, page) =>\n acc + (page.responseBody?.queryResult?.queryResults.rows?.length ?? 0),\n 0,\n )\n\n if (rowsInLastPage === 0) {\n return undefined\n }\n\n // A short page means we've reached the end, even when total hits is missing or stale.\n if (requestedPageSize != null && rowsInLastPage < requestedPageSize) {\n return undefined\n }\n\n if (totalRows != null && fetchedRows >= totalRows) {\n return undefined\n }\n\n return fetchedRows\n}\n\n/**\n * Converts a QueryBundleRequest (used internally for state management) into a SearchIndexQuery\n * that can be sent to the SearchQueryServicesApi.\n *\n * Mapping:\n * - columnModels (facetType 'enumeration') → aggregations (OpenSearch terms or filter-wrapped aggregations)\n * - query.selectedFacets (enumeration) → post_filter (terms clauses) + per-facet filter aggregations\n * - query.selectedFacets (range) → post_filter (range clauses) + included in all per-facet aggregation filters\n * - query.additionalFilters (TextMatchesQueryFilter) → multi_match query clause\n * - query.sort → sort (SortItem { column, direction } → [{ column: 'asc'|'desc' }])\n * - query.limit → size\n * - query.offset → from\n * - partMask → dropped (SearchIndexQuery has no bitmask concept)\n *\n * Per-facet filter bucketing (OpenSearch \"faceted search\" pattern):\n * When any facet selections are active, each enum aggregation is wrapped in an OpenSearch\n * filter aggregation whose filter is the AND of all OTHER selections (its own selection is\n * dropped so the column stays wide). post_filter = AND of ALL selections, narrowing hits/total\n * only. This lets each selected facet column show the full set of option counts while all\n * other columns narrow to the selection — in a single request.\n */\nexport function toSearchIndexQuery(\n queryBundleRequest: QueryBundleRequest,\n searchIndexId: string,\n columnModels?: ColumnModel[],\n searchQueryConfig?: SearchQueryConfig,\n): SearchIndexQuery {\n // Build aggregations from ColumnModels that have facetType 'enumeration'.\n // Range columns are excluded: we synthesize FacetColumnResultRange entries client-side.\n // Aggregations are built lazily after filterClauses is populated so the per-facet\n // filter wrapping logic has access to all active selections.\n\n // Build filter clauses from enumeration selectedFacets\n const filterClauses: FilterClause[] = []\n\n queryBundleRequest.query.selectedFacets\n ?.filter(\n (f): f is FacetColumnValuesRequest =>\n f.concreteType === FACET_COLUMN_VALUES_REQUEST_CONCRETE_TYPE_VALUE,\n )\n .forEach(f => {\n filterClauses.push({ terms: { [f.columnName]: f.facetValues ?? [] } })\n })\n\n // Build range filter clauses from range selectedFacets.\n // DATE columns: the Range UI emits formatted dates (\"YYYY-MM-DD\") but OpenSearch\n // expects unix millisecond timestamps. Convert here.\n queryBundleRequest.query.selectedFacets\n ?.filter(\n (f): f is FacetColumnRangeRequest =>\n f.concreteType === FACET_COLUMN_RANGE_REQUEST_CONCRETE_TYPE_VALUE,\n )\n .forEach(f => {\n // \"Not Assigned\" selection: the RangeFacetFilter UI sets both bounds to\n // VALUE_NOT_SET. Translate to an OpenSearch \"field missing\" clause rather\n // than emitting the sentinel string as a range bound (which would fail\n // with NumberFormatException on numeric/date fields).\n if (f.min === VALUE_NOT_SET && f.max === VALUE_NOT_SET) {\n filterClauses.push({\n bool: { must_not: [{ exists: { field: f.columnName } }] },\n })\n return\n }\n const columnType = columnModels?.find(\n cm => cm.name === f.columnName,\n )?.columnType\n const toApiValue = (v: string | undefined): string | undefined => {\n if (!v || columnType !== 'DATE') return v\n // Convert \"YYYY-MM-DD\" → unix ms; leave already-numeric values untouched\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(v)) {\n return String(dayjs(v).valueOf())\n }\n return v\n }\n const rangeClause: { gte?: string; lte?: string } = {}\n const min = toApiValue(f.min)\n const max = toApiValue(f.max)\n if (min !== undefined) rangeClause.gte = min\n if (max !== undefined) rangeClause.lte = max\n filterClauses.push({ range: { [f.columnName]: rangeClause } })\n })\n\n // Extract queryText by concatenating all TextMatchesQueryFilter searchExpression values\n const textMatchesExpressions = queryBundleRequest.query.additionalFilters\n ?.filter(\n (f): f is TextMatchesQueryFilter =>\n f.concreteType === TEXT_MATCHES_QUERY_FILTER_CONCRETE_TYPE_VALUE,\n )\n .map(f => f.searchExpression)\n .filter((expr): expr is string => expr != null && expr.length > 0)\n const queryText =\n textMatchesExpressions && textMatchesExpressions.length > 0\n ? textMatchesExpressions.join(' ')\n : undefined\n\n // Build the OpenSearch query DSL (text search only; facet selections go to post_filter).\n let query: SearchQueryDSL\n if (queryText) {\n query = buildQueryClause(queryText, searchQueryConfig)\n } else {\n query = { match_all: {} }\n }\n\n // post_filter narrows hits/total by ALL active facet selections without affecting the\n // aggregation counts. Each enum aggregation independently applies a per-facet filter\n // (see aggregationEntries below) so each column stays wide for its own selection while\n // all other columns narrow.\n let postFilter: SelectionFilter | undefined\n if (filterClauses.length === 1) {\n postFilter = filterClauses[0]\n } else if (filterClauses.length > 1) {\n postFilter = { bool: { must: filterClauses } }\n }\n\n // Build aggregations. When selections are active, wrap each term's aggregation in an\n // OpenSearch filter aggregation whose filter is ALL selections EXCEPT the one for this\n // column. That keeps the column's own options wide while the others narrow.\n const aggregationEntries = columnModels\n ?.filter(cm => cm.facetType === 'enumeration')\n .map(cm => {\n const termsAgg: TermsAggregation = { field: cm.name, size: 100 }\n if (cm.facetSortConfig?.property != null) {\n // Map FacetColumnSortProperty ('VALUE'|'FREQUENCY') → OpenSearch order key ('_key'|'_count')\n const orderKey =\n cm.facetSortConfig.property === 'VALUE' ? '_key' : '_count'\n const orderDir =\n cm.facetSortConfig.direction === 'DESC' ? 'desc' : 'asc'\n termsAgg.order = { [orderKey]: orderDir }\n }\n\n if (filterClauses.length === 0) {\n // Baseline (no selections): plain terms aggregation, no wrapping needed.\n return [cm.name, { terms: termsAgg }] as [string, Aggregation]\n }\n\n // Per-facet filter bucketing: exclude this column's own selection from the filter\n // so it stays wide, while all other active selections narrow the counts.\n const otherClauses = filterClauses.filter(clause => {\n if ('terms' in clause) return !(cm.name in clause.terms)\n if ('range' in clause) return !(cm.name in clause.range)\n // \"Not Assigned\" range clause: identify by the field inside must_not.exists.\n if ('bool' in clause)\n return clause.bool.must_not[0]?.exists?.field !== cm.name\n return true\n })\n const aggFilter: SelectionFilter =\n otherClauses.length === 0\n ? { match_all: {} }\n : otherClauses.length === 1\n ? otherClauses[0]\n : { bool: { must: otherClauses } }\n return [\n cm.name,\n {\n filter: aggFilter,\n aggregations: { [cm.name]: { terms: termsAgg } },\n } as unknown as Aggregation,\n ] as [string, Aggregation]\n })\n const aggregations =\n aggregationEntries && aggregationEntries.length > 0\n ? Object.fromEntries(aggregationEntries)\n : undefined\n\n // Map SortItem[] → OpenSearch sort shape: [{ columnName: 'asc'|'desc' }, ...]\n // filtering out entries with no direction (which mean \"remove sort\").\n const sortEntries = queryBundleRequest.query.sort\n ?.filter(\n (s): s is SortItem & { direction: 'ASC' | 'DESC' } =>\n s.direction === 'ASC' || s.direction === 'DESC',\n )\n .map(s => ({ [s.column]: s.direction.toLowerCase() }))\n\n const searchQuery: SearchSearchQuery = {\n query: query as unknown as DslQuery,\n post_filter: postFilter ? (postFilter as unknown as DslQuery) : undefined,\n aggregations,\n sort: sortEntries?.length ? sortEntries : undefined,\n size: queryBundleRequest.query.limit,\n from: queryBundleRequest.query.offset,\n }\n\n return {\n concreteType:\n 'org.sagebionetworks.repo.model.search.table.SearchIndexQuery' as const,\n searchIndexId,\n searchQuery,\n }\n}\n\n/**\n * Converts a SearchQueryResults (the actual API response) into a QueryResultBundle\n * so that existing subcomponents (FacetFilterControls, RowSetView, etc.) work unchanged.\n *\n * Mapping:\n * - totalHits → queryCount\n * - hits → queryResult.queryResults (rows derived from hit.rowId/rowVersion/fields)\n * - aggregationResults → facets (parsed from opaque OpenSearch aggregation JSON)\n */\nexport function searchQueryResultsToQueryResultBundle(\n results: SearchQueryResults | undefined,\n query: SearchIndexQuery,\n columnModels?: ColumnModel[],\n selectedFacets?: (FacetColumnValuesRequest | FacetColumnRangeRequest)[],\n): QueryResultBundle {\n if (!results) {\n return {\n concreteType: 'org.sagebionetworks.repo.model.table.QueryResultBundle',\n }\n }\n\n // Derive column names from the server-returned selectColumns. Both the row data\n // query and the metadata query request SELECT_COLUMNS, so this is always present.\n const fieldNames: string[] =\n results.selectColumns\n ?.map(col => col.name)\n .filter((n): n is string => !!n) ?? []\n\n const headers = fieldNames.map(name => ({\n name,\n columnType:\n columnModels?.find(cm => cm.name === name)?.columnType ??\n ('STRING' as const),\n }))\n\n const rows = (results.hits ?? []).map(hit => ({\n rowId: hit.rowId,\n versionNumber: hit.rowVersion,\n values: fieldNames.map(name => {\n return hit.fields?.find(f => f.name === name)?.value ?? null\n }),\n }))\n\n const queryResult =\n headers.length > 0 || rows.length > 0\n ? {\n concreteType:\n 'org.sagebionetworks.repo.model.table.QueryResult' as const,\n queryResults: {\n concreteType:\n 'org.sagebionetworks.repo.model.table.RowSet' as const,\n tableId: query.searchIndexId ?? '',\n etag: '',\n headers,\n rows,\n },\n }\n : undefined\n\n // Parse aggregationResults (opaque OpenSearch aggregation JSON) into FacetColumnResultValues.\n // Each key in aggregationResults corresponds to a column name whose aggregation was requested\n // in toSearchIndexQuery. Two response shapes are possible:\n //\n // Plain terms (no active selections):\n // { buckets: [{ key: unknown, doc_count: number }] }\n //\n // Filter-wrapped terms (per-facet filter bucketing, selections active):\n // { doc_count: number, \"<type>#columnName\": { buckets: [...] } }\n //\n // OpenSearch prefixes the aggregation type to the name in the response. The prefix depends\n // on the field type: \"sterms\" for string fields, \"lterms\" for long/integer fields, etc.\n // We detect the shape by looking for any \"type#columnName\" key with a buckets property.\n const aggregationResults = results.aggregationResults as unknown as Record<\n string,\n OpenSearchTermsAgg | Record<string, OpenSearchTermsAgg>\n > | null\n const rawFacets: FacetColumnResultValues[] = Object.entries(\n aggregationResults ?? {},\n ).map(([columnName, agg]) => {\n // Resolve the actual terms result: OpenSearch prefixes the agg type when it is\n // nested inside a filter aggregation, producing a \"sterms#columnName\" key.\n // Fall back to a direct [columnName] match (defensive), then to the agg itself\n // for the plain (no-selection) terms shape.\n const aggRecord = agg as Record<string, OpenSearchTermsAgg>\n // OpenSearch prefixes the aggregation type to the name when nested inside a filter\n // aggregation. For string fields this is \"sterms#columnName\"; for long/integer fields\n // it is \"lterms#columnName\". Rather than hardcoding known prefixes, look for any key\n // ending in \"#columnName\" so that future numeric types (e.g. double) also work.\n const nestedKey = Object.keys(aggRecord).find(key =>\n key.endsWith(`#${columnName}`),\n )\n const nested =\n (nestedKey !== undefined ? aggRecord[nestedKey] : undefined) ??\n aggRecord[columnName]\n const termsAgg: OpenSearchTermsAgg =\n nested?.buckets !== undefined ? nested : (agg as OpenSearchTermsAgg)\n const selectedValues =\n selectedFacets\n ?.filter(\n (f): f is FacetColumnValuesRequest =>\n f.concreteType ===\n FACET_COLUMN_VALUES_REQUEST_CONCRETE_TYPE_VALUE &&\n f.columnName === columnName,\n )\n .flatMap(f => f.facetValues ?? []) ?? []\n return {\n concreteType:\n 'org.sagebionetworks.repo.model.table.FacetColumnResultValues' as const,\n facetType: 'enumeration' as const,\n columnName,\n facetValues: (termsAgg.buckets ?? []).map(b => ({\n value: String(b.key),\n count: b.doc_count,\n isSelected: selectedValues.includes(String(b.key)),\n })),\n }\n })\n\n // Synthesize FacetColumnResultRange for range-type DATE/DOUBLE/INTEGER columns. The Search\n // API's aggregations only produce terms (enumeration) results; range stats are unavailable\n // server-side. columnMin/columnMax are left empty; selectedMin/selectedMax are taken\n // directly from selectedFacets so any applied range filter is still reflected in the UI.\n // Note: for INTEGER columns the RangeSlider requires real bounds, so the UI falls back to a\n // plain Range number input when columnMin/columnMax are absent.\n const syntheticRangeFacets: FacetColumnResultRange[] = (columnModels ?? [])\n .filter(\n cm =>\n cm.facetType === 'range' &&\n (cm.columnType === 'DATE' ||\n cm.columnType === 'DOUBLE' ||\n cm.columnType === 'INTEGER') &&\n !rawFacets.some(f => f.columnName === cm.name),\n )\n .map(cm => {\n const rangeFilter = selectedFacets?.find(\n (f): f is FacetColumnRangeRequest =>\n f.concreteType === FACET_COLUMN_RANGE_REQUEST_CONCRETE_TYPE_VALUE &&\n f.columnName === cm.name,\n )\n return {\n concreteType:\n 'org.sagebionetworks.repo.model.table.FacetColumnResultRange' as const,\n facetType: 'range' as const,\n columnName: cm.name,\n columnMin: '',\n columnMax: '',\n selectedMin: rangeFilter?.min,\n selectedMax: rangeFilter?.max,\n }\n })\n\n const allFacets: NonNullable<QueryResultBundle['facets']> = [\n ...(rawFacets ?? []),\n ...syntheticRangeFacets,\n ]\n\n // Reorder all facets to match the columnModels order so downstream components\n // (e.g. FacetFilterControls) render facets in the expected column order.\n const orderedFacets: QueryResultBundle['facets'] = columnModels\n ? [\n ...allFacets\n .filter(f => columnModels.some(cm => cm.name === f.columnName))\n .sort(\n (a, b) =>\n columnModels.findIndex(cm => cm.name === a.columnName) -\n columnModels.findIndex(cm => cm.name === b.columnName),\n ),\n ...allFacets.filter(\n f => !columnModels.some(cm => cm.name === f.columnName),\n ),\n ]\n : allFacets\n\n const maxScore = results.hits?.[0]?.score\n return {\n concreteType: 'org.sagebionetworks.repo.model.table.QueryResultBundle',\n queryCount: results.totalHits,\n selectColumns: headers,\n queryResult,\n facets: orderedFacets,\n ...(maxScore !== undefined && { maxScore }),\n } as QueryResultBundle\n}\n\nexport function getSearchQueryUseQueryOptions(\n queryBundleRequest: QueryBundleRequest,\n keyFactory: KeyFactory,\n accessToken: string | undefined,\n searchIndexId: string,\n columnModels?: ColumnModel[],\n searchQueryConfig?: SearchQueryConfig,\n): SearchTableQueryUseQueryOptions {\n const baseQuery: SearchIndexQuery = toSearchIndexQuery(\n queryBundleRequest,\n searchIndexId,\n columnModels,\n searchQueryConfig,\n )\n const rowDataQuery: SearchIndexQuery = {\n ...baseQuery,\n searchQuery: {\n ...baseQuery.searchQuery,\n // Omit aggregations from the row data query: facet aggregations are only needed\n // in the metadata query. Removing them here keeps the row data query key stable\n // while column models are loading, preventing an unnecessary re-fetch (and a brief\n // loading flash) when column models first arrive.\n aggregations: undefined,\n },\n // Request SELECT_COLUMNS so headers are available for row rendering.\n // HITS is returned by default even when responseParts is specified.\n responseParts: new Set(['HITS', 'SELECT_COLUMNS'] as const),\n }\n const metadataQuery: SearchIndexQuery = {\n ...baseQuery,\n searchQuery: {\n ...baseQuery.searchQuery,\n // Fetch only 1 hit: we need just the top hit's score for tab auto-selection.\n // Aggregation counts are independent of size.\n size: 1,\n },\n // Request all opt-in parts needed for the metadata response:\n // HITS → exposes the top hit's relevance score for tab auto-selection\n // TOTAL_HITS → queryCount (drives tab count / spinner resolution)\n // SELECT_COLUMNS → column headers for the table\n // Aggregation results are returned automatically when aggregations is set on the query.\n responseParts: new Set(['HITS', 'TOTAL_HITS', 'SELECT_COLUMNS'] as const),\n }\n\n // Convert responseParts Set → sorted array for query key building.\n // JSON.stringify(new Set([...])) === '{}', so two queries whose only difference is\n // their responseParts would hash to the same key and share a cache entry.\n // Using a sorted array gives each query a distinct, stable key.\n const toKeyShape = (query: SearchIndexQuery) => ({\n ...query,\n responseParts: query.responseParts\n ? [...query.responseParts].sort()\n : undefined,\n })\n\n // Fetches from the SearchQueryServicesApi and converts the SearchQueryResults responseBody\n // into QueryResultBundle shape. This allows all downstream shared components\n // (UseRowSet, QueryVisualizationWrapper, etc.) to access data.responseBody.*\n // using the same patterns as regular table queries (queryResult.queryResults, selectColumns, etc.).\n const fetchAndConvert = async (query: SearchIndexQuery) => {\n const asyncStatus = await SynapseClient.getSearchQueryAsyncJobResults(\n query,\n accessToken,\n )\n return {\n ...asyncStatus,\n responseBody: searchQueryResultsToQueryResultBundle(\n asyncStatus.responseBody,\n query,\n columnModels,\n queryBundleRequest.query.selectedFacets as\n | (FacetColumnValuesRequest | FacetColumnRangeRequest)[]\n | undefined,\n ),\n }\n }\n\n // `as unknown as` is necessary because SearchIndexQuery.responseParts is a Set but\n // toKeyShape converts it to a sorted string[] for stable cache key serialisation.\n const rowDataQueryOptions: SearchTableQueryUseQueryOptions['rowDataQueryOptions'] =\n {\n ...tableQueryUseQueryDefaults,\n queryKey: keyFactory.getSearchQueryResultWithAsyncStatusQueryKey(\n toKeyShape(rowDataQuery) as unknown as SearchIndexQuery,\n false,\n ),\n queryFn: () => fetchAndConvert(rowDataQuery),\n select: (\n data: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n ): RowSet | undefined => data.responseBody?.queryResult?.queryResults,\n }\n\n const rowDataInfiniteQueryOptions: SearchTableQueryUseQueryOptions['rowDataInfiniteQueryOptions'] =\n {\n ...tableQueryUseQueryDefaults,\n queryKey: keyFactory.getSearchQueryResultWithAsyncStatusQueryKey(\n toKeyShape(rowDataQuery) as unknown as SearchIndexQuery,\n true,\n ),\n initialPageParam: 0 as number | string | undefined,\n getNextPageParam: (\n lastPage: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n allPages: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>[],\n ): number | string | undefined => {\n return getNextPageParamForSearchQueryResults(lastPage, allPages)\n },\n queryFn: (context: { pageParam?: number | string }) => {\n const offset =\n typeof context.pageParam === 'string'\n ? parseInt(context.pageParam)\n : (context.pageParam ?? 0)\n const requestForPage: SearchIndexQuery = {\n ...rowDataQuery,\n searchQuery: {\n ...rowDataQuery.searchQuery,\n from: offset,\n },\n }\n return fetchAndConvert(requestForPage)\n },\n select: (\n data: InfiniteData<\n AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>\n >,\n ) => data,\n }\n\n const queryMetadataQueryOptions: SearchTableQueryUseQueryOptions['queryMetadataQueryOptions'] =\n {\n ...tableQueryUseQueryDefaults,\n queryKey: keyFactory.getSearchQueryResultWithAsyncStatusQueryKey(\n toKeyShape(metadataQuery) as unknown as SearchIndexQuery,\n false,\n ),\n queryFn: () => fetchAndConvert(metadataQuery),\n select: (\n data: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n ): Omit<QueryResultBundle, 'queryResult'> => {\n const result = omit(data.responseBody ?? {}, 'queryResult') as Omit<\n QueryResultBundle,\n 'queryResult'\n >\n return columnModels ? { ...result, columnModels } : result\n },\n }\n\n return {\n rowDataQueryOptions,\n rowDataInfiniteQueryOptions,\n queryMetadataQueryOptions,\n }\n}\n\n/**\n * Returns a stable callback that fetches autocomplete suggestions for a search input\n * using the synchronous `POST /search/autocomplete` endpoint.\n *\n * The query is built from the current `searchIndexId` and `autocompleteFieldName`:\n * - `searchQuery.query` is a `match_bool_prefix` clause scoped to `autocompleteFieldName`\n * - `searchQuery._source` is set to `[autocompleteFieldName]` so only that field is returned\n *\n * When `autocompleteFieldName` is undefined, the returned callback immediately returns `[]`.\n */\nexport function useGetSuggestionsForSearchIndex(\n searchIndexId: string,\n autocompleteFieldName: string | undefined,\n): (searchText: string) => Promise<string[]> {\n const { synapseClient } = useSynapseContext()\n\n return useCallback(\n async (searchText: string): Promise<string[]> => {\n if (!autocompleteFieldName) return []\n const trimmed = searchText.trim()\n if (!trimmed) return []\n const results =\n await synapseClient.searchManagementServicesClient.postRepoV1SearchAutocomplete(\n {\n searchAutocompleteRequest: {\n searchIndexId,\n searchQuery: {\n query: {\n match_bool_prefix: {\n [autocompleteFieldName]: { query: trimmed },\n },\n },\n _source: { includes: [autocompleteFieldName] },\n },\n },\n },\n )\n const suggestions: string[] = []\n for (const hit of results.hits ?? []) {\n for (const field of hit.fields ?? []) {\n if (field.value) {\n suggestions.push(field.value)\n }\n }\n }\n return suggestions\n },\n [synapseClient, searchIndexId, autocompleteFieldName],\n )\n}\n\n/**\n * Returns TanStack Query options that call the SearchQueryServicesApi (instead of the\n * standard Table Query API) but return data in the same QueryResultBundle format, allowing\n * all existing subcomponents (FacetFilterControls, RowSetView, etc.) to work unchanged.\n */\nexport function useSearchQueryUseQueryOptions(\n queryBundleRequest: QueryBundleRequest,\n searchIndexId: string,\n synapseId?: string,\n searchQueryConfig?: SearchQueryConfig,\n): SearchTableQueryUseQueryOptions {\n const { keyFactory, accessToken } = useSynapseContext()\n\n const { data: entityBundle } = useGetEntityBundle(synapseId, undefined, {\n includeTableBundle: true,\n })\n const columnModels = entityBundle?.tableBundle?.columnModels\n\n return useMemo(\n () =>\n getSearchQueryUseQueryOptions(\n queryBundleRequest,\n keyFactory,\n accessToken,\n searchIndexId,\n columnModels,\n searchQueryConfig,\n ),\n [\n keyFactory,\n accessToken,\n queryBundleRequest,\n searchIndexId,\n columnModels,\n searchQueryConfig,\n ],\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SearchQueryUseQueryOptions.js","names":[],"sources":["../../../src/components/SearchQueryWrapper/SearchQueryUseQueryOptions.ts"],"sourcesContent":["import * as SynapseClient from '@/synapse-client/SynapseClient'\nimport { tableQueryUseQueryDefaults } from '@/synapse-queries'\nimport { useGetEntityBundle } from '@/synapse-queries/entity/useEntityBundle'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport {\n AsynchronousJobStatus,\n ColumnModel,\n FACET_COLUMN_RANGE_REQUEST_CONCRETE_TYPE_VALUE,\n FacetColumnRangeRequest,\n FacetColumnResultRange,\n FacetColumnResultValues,\n FACET_COLUMN_VALUES_REQUEST_CONCRETE_TYPE_VALUE,\n FacetColumnValuesRequest,\n QueryBundleRequest,\n QueryResultBundle,\n RowSet,\n SortItem,\n TEXT_MATCHES_QUERY_FILTER_CONCRETE_TYPE_VALUE,\n TextMatchesQueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport type {\n Aggregation,\n DslQuery,\n SearchIndexQuery,\n SearchQueryResults,\n SearchSearchQuery,\n SynapseClientError,\n TermsAggregation,\n} from '@sage-bionetworks/synapse-client'\nimport {\n InfiniteData,\n QueryKey,\n UseInfiniteQueryOptions,\n UseSuspenseQueryOptions,\n} from '@tanstack/react-query'\nimport { omit } from 'lodash-es'\nimport dayjs from 'dayjs'\nimport { useCallback, useMemo } from 'react'\nimport { KeyFactory } from '@/synapse-queries/KeyFactory'\nimport { SYNAPSE_ENTITY_ID_TOKEN_REGEX } from '@/utils/functions/RegularExpressions'\nimport { VALUE_NOT_SET } from '@/utils/SynapseConstants'\n\n// ─── Local OpenSearch DSL types ─────────────────────────────────────────────\n// Narrow typings for the OpenSearch structures we build (query DSL, sort) and\n// parse (aggregation results). These are intentionally minimal – only the fields\n// we actually produce/consume are declared. Aggregation types use the generated\n// Aggregation/TermsAggregation types from @sage-bionetworks/synapse-client.\n\nexport type SearchQueryStrategy =\n | 'MULTI_MATCH'\n | 'MULTI_MATCH_BEST_FIELDS'\n | 'MULTI_MATCH_CROSS_FIELDS'\n | 'SIMPLE_QUERY_STRING'\n | 'PHRASE_PREFIX'\n | 'BOOSTED_FUZZY'\n\nexport type SearchQueryConfig = {\n /**\n * Which OpenSearch query clause to emit when the user has typed a search term.\n * Defaults to 'MULTI_MATCH' (all fields, fuzziness AUTO — today's behavior).\n */\n queryStrategy?: SearchQueryStrategy\n /**\n * Per-field relevance multipliers. Serialized to the OpenSearch `fields` array\n * as e.g. `[\"resourceName^5\", \"synonyms^4\"]`. When omitted, all fields are\n * queried at equal weight.\n */\n fieldBoosts?: Record<string, number>\n /** Whether to append `*` to fieldBoosts at neutral weight. Defaults to true. */\n includeUnlistedFields?: boolean\n /**\n * Fuzziness override. Overrides the per-strategy default. Leave undefined to\n * use each strategy's natural default (AUTO for MULTI_MATCH/BOOSTED_FUZZY,\n * none for the rest).\n */\n fuzziness?: string\n}\n\ntype FilterClause =\n | { terms: Record<string, string[]> }\n | { range: Record<string, { gte?: string; lte?: string }> }\n // \"Field is not set\" — emitted when a range facet selects \"Not Assigned\".\n | { bool: { must_not: [{ exists: { field: string } }] } }\n\ntype MultiMatchClause = {\n multi_match: {\n query: string\n fuzziness?: string\n type?: 'best_fields' | 'cross_fields' | 'phrase_prefix'\n fields?: string[]\n }\n}\n\ntype SimpleQueryStringClause = {\n simple_query_string: { query: string; fields?: string[] }\n}\n\ntype BoolQuery = {\n bool: {\n must?: MultiMatchClause[] | FilterClause[]\n filter?: FilterClause[]\n }\n}\n\n// post_filter / per-facet aggregation filter: a single FilterClause, match_all, or AND of many.\ntype SelectionFilter =\n | FilterClause\n | { match_all: Record<never, never> }\n | { bool: { must: FilterClause[] } }\n\ntype SearchQueryDSL =\n | MultiMatchClause\n | SimpleQueryStringClause\n | { match_all: Record<never, never> }\n | BoolQuery\n\nfunction resolveFields(\n fieldBoosts: Record<string, number> | undefined,\n includeUnlistedFields: boolean,\n): string[] | undefined {\n if (!fieldBoosts) return undefined\n const boosts =\n includeUnlistedFields && !('*' in fieldBoosts)\n ? { ...fieldBoosts, '*': 1 }\n : fieldBoosts\n return Object.entries(boosts).map(([field, boost]) =>\n boost === 1 ? field : `${field}^${boost}`,\n )\n}\n\n/**\n * Returns true if the query text contains at least one double-quoted phrase\n * (e.g. `\"cancer genomics\"`). OpenSearch `multi_match` does not understand the\n * `\"phrase\"` operator — only `simple_query_string` does — so callers use this\n * to route quoted queries appropriately.\n */\nfunction containsQuotedPhrase(text: string): boolean {\n // Match a pair of double-quotes enclosing at least one non-quote character.\n return /\"[^\"]+\"/u.test(text)\n}\n\n/**\n * Returns true if the query text contains a Synapse entity ID token\n * (e.g. `syn12345` or `syn12345.4`). `multi_match` with fuzziness AUTO can match\n * neighbouring IDs (e.g. `syn12345` → `syn12344`), which is almost never desired\n * for identifier lookups. Callers use this to route ID-containing queries to\n * `simple_query_string`, which does not apply fuzziness.\n */\nfunction containsSynapseId(text: string): boolean {\n return SYNAPSE_ENTITY_ID_TOKEN_REGEX.test(text)\n}\n\nfunction buildQueryClause(\n queryText: string,\n config: SearchQueryConfig = {},\n): MultiMatchClause | SimpleQueryStringClause {\n const {\n queryStrategy = 'MULTI_MATCH',\n fieldBoosts,\n includeUnlistedFields = true,\n fuzziness,\n } = config\n const fields = resolveFields(fieldBoosts, includeUnlistedFields)\n\n // Route to simple_query_string when the query needs exact-token treatment:\n // - a double-quoted phrase (multi_match can't honour the phrase operator), or\n // - a Synapse entity ID (fuzziness would match neighbouring IDs).\n // simple_query_string doesn't apply fuzziness by default, so both cases work.\n if (containsQuotedPhrase(queryText) || containsSynapseId(queryText)) {\n return {\n simple_query_string: {\n query: queryText,\n ...(fields ? { fields } : {}),\n },\n }\n }\n\n switch (queryStrategy) {\n case 'SIMPLE_QUERY_STRING':\n return {\n simple_query_string: {\n query: queryText,\n ...(fields ? { fields } : {}),\n },\n }\n case 'MULTI_MATCH_BEST_FIELDS':\n return {\n multi_match: {\n query: queryText,\n type: 'best_fields',\n ...(fields ? { fields } : {}),\n ...(fuzziness ? { fuzziness } : {}),\n },\n }\n case 'MULTI_MATCH_CROSS_FIELDS':\n return {\n multi_match: {\n query: queryText,\n type: 'cross_fields',\n ...(fields ? { fields } : {}),\n ...(fuzziness ? { fuzziness } : {}),\n },\n }\n case 'PHRASE_PREFIX':\n return {\n multi_match: {\n query: queryText,\n type: 'phrase_prefix',\n ...(fields ? { fields } : {}),\n },\n }\n case 'BOOSTED_FUZZY':\n return {\n multi_match: {\n query: queryText,\n ...(fields ? { fields } : {}),\n fuzziness: fuzziness ?? 'AUTO',\n },\n }\n default: // 'MULTI_MATCH'\n return {\n multi_match: {\n query: queryText,\n fuzziness: fuzziness ?? 'AUTO',\n },\n }\n }\n}\n\ntype OpenSearchBucket = { key: unknown; doc_count: number }\ntype OpenSearchTermsAgg = { buckets?: OpenSearchBucket[] }\n\n/**\n * TanStack Query options for the SearchQueryServicesApi, mirroring TableQueryUseQueryOptions\n * but parameterised over SearchIndexQuery rather than QueryBundleRequest. This eliminates\n * `as unknown as` casts inside the implementation. A single cast to TableQueryUseQueryOptions\n * is still required at the boundary where SearchQueryWrapper assigns these into QueryContextType.\n */\nexport type SearchTableQueryUseQueryOptions = {\n rowDataQueryOptions: UseSuspenseQueryOptions<\n AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n SynapseClientError,\n RowSet | undefined\n >\n rowDataInfiniteQueryOptions: UseInfiniteQueryOptions<\n AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n SynapseClientError,\n InfiniteData<AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>>,\n QueryKey,\n string | number | undefined\n >\n queryMetadataQueryOptions: UseSuspenseQueryOptions<\n AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n SynapseClientError,\n Omit<QueryResultBundle, 'queryResult'>\n >\n}\n\nexport function getNextPageParamForSearchQueryResults(\n lastPage: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n allPages: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>[],\n): number | string | undefined {\n const rowsInLastPage =\n lastPage.responseBody?.queryResult?.queryResults.rows?.length ?? 0\n const requestedPageSize = lastPage.requestBody?.searchQuery?.size\n const totalRows = lastPage.responseBody?.queryCount\n const fetchedRows = allPages.reduce(\n (acc, page) =>\n acc + (page.responseBody?.queryResult?.queryResults.rows?.length ?? 0),\n 0,\n )\n\n if (rowsInLastPage === 0) {\n return undefined\n }\n\n // A short page means we've reached the end, even when total hits is missing or stale.\n if (requestedPageSize != null && rowsInLastPage < requestedPageSize) {\n return undefined\n }\n\n if (totalRows != null && fetchedRows >= totalRows) {\n return undefined\n }\n\n return fetchedRows\n}\n\n/**\n * Converts a QueryBundleRequest (used internally for state management) into a SearchIndexQuery\n * that can be sent to the SearchQueryServicesApi.\n *\n * Mapping:\n * - columnModels (facetType 'enumeration') → aggregations (OpenSearch terms or filter-wrapped aggregations)\n * - query.selectedFacets (enumeration) → post_filter (terms clauses) + per-facet filter aggregations\n * - query.selectedFacets (range) → post_filter (range clauses) + included in all per-facet aggregation filters\n * - query.additionalFilters (TextMatchesQueryFilter) → multi_match query clause\n * - query.sort → sort (SortItem { column, direction } → [{ column: 'asc'|'desc' }])\n * - query.limit → size\n * - query.offset → from\n * - partMask → dropped (SearchIndexQuery has no bitmask concept)\n *\n * Per-facet filter bucketing (OpenSearch \"faceted search\" pattern):\n * When any facet selections are active, each enum aggregation is wrapped in an OpenSearch\n * filter aggregation whose filter is the AND of all OTHER selections (its own selection is\n * dropped so the column stays wide). post_filter = AND of ALL selections, narrowing hits/total\n * only. This lets each selected facet column show the full set of option counts while all\n * other columns narrow to the selection — in a single request.\n */\nexport function toSearchIndexQuery(\n queryBundleRequest: QueryBundleRequest,\n searchIndexId: string,\n columnModels?: ColumnModel[],\n searchQueryConfig?: SearchQueryConfig,\n): SearchIndexQuery {\n // Build aggregations from ColumnModels that have facetType 'enumeration'.\n // Range columns are excluded: we synthesize FacetColumnResultRange entries client-side.\n // Aggregations are built lazily after filterClauses is populated so the per-facet\n // filter wrapping logic has access to all active selections.\n\n // Build filter clauses from enumeration selectedFacets\n const filterClauses: FilterClause[] = []\n\n queryBundleRequest.query.selectedFacets\n ?.filter(\n (f): f is FacetColumnValuesRequest =>\n f.concreteType === FACET_COLUMN_VALUES_REQUEST_CONCRETE_TYPE_VALUE,\n )\n .forEach(f => {\n filterClauses.push({ terms: { [f.columnName]: f.facetValues ?? [] } })\n })\n\n // Build range filter clauses from range selectedFacets.\n // DATE columns: the Range UI emits formatted dates (\"YYYY-MM-DD\") but OpenSearch\n // expects unix millisecond timestamps. Convert here.\n queryBundleRequest.query.selectedFacets\n ?.filter(\n (f): f is FacetColumnRangeRequest =>\n f.concreteType === FACET_COLUMN_RANGE_REQUEST_CONCRETE_TYPE_VALUE,\n )\n .forEach(f => {\n // \"Not Assigned\" selection: the RangeFacetFilter UI sets both bounds to\n // VALUE_NOT_SET. Translate to an OpenSearch \"field missing\" clause rather\n // than emitting the sentinel string as a range bound (which would fail\n // with NumberFormatException on numeric/date fields).\n if (f.min === VALUE_NOT_SET && f.max === VALUE_NOT_SET) {\n filterClauses.push({\n bool: { must_not: [{ exists: { field: f.columnName } }] },\n })\n return\n }\n const columnType = columnModels?.find(\n cm => cm.name === f.columnName,\n )?.columnType\n const toApiValue = (v: string | undefined): string | undefined => {\n if (!v || columnType !== 'DATE') return v\n // Convert \"YYYY-MM-DD\" → unix ms; leave already-numeric values untouched\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(v)) {\n return String(dayjs(v).valueOf())\n }\n return v\n }\n const rangeClause: { gte?: string; lte?: string } = {}\n const min = toApiValue(f.min)\n const max = toApiValue(f.max)\n if (min !== undefined) rangeClause.gte = min\n if (max !== undefined) rangeClause.lte = max\n filterClauses.push({ range: { [f.columnName]: rangeClause } })\n })\n\n // Extract queryText by concatenating all TextMatchesQueryFilter searchExpression values\n const textMatchesExpressions = queryBundleRequest.query.additionalFilters\n ?.filter(\n (f): f is TextMatchesQueryFilter =>\n f.concreteType === TEXT_MATCHES_QUERY_FILTER_CONCRETE_TYPE_VALUE,\n )\n .map(f => f.searchExpression)\n .filter((expr): expr is string => expr != null && expr.length > 0)\n const queryText =\n textMatchesExpressions && textMatchesExpressions.length > 0\n ? textMatchesExpressions.join(' ')\n : undefined\n\n // Build the OpenSearch query DSL (text search only; facet selections go to post_filter).\n let query: SearchQueryDSL\n if (queryText) {\n query = buildQueryClause(queryText, searchQueryConfig)\n } else {\n query = { match_all: {} }\n }\n\n // post_filter narrows hits/total by ALL active facet selections without affecting the\n // aggregation counts. Each enum aggregation independently applies a per-facet filter\n // (see aggregationEntries below) so each column stays wide for its own selection while\n // all other columns narrow.\n let postFilter: SelectionFilter | undefined\n if (filterClauses.length === 1) {\n postFilter = filterClauses[0]\n } else if (filterClauses.length > 1) {\n postFilter = { bool: { must: filterClauses } }\n }\n\n // Build aggregations. When selections are active, wrap each term's aggregation in an\n // OpenSearch filter aggregation whose filter is ALL selections EXCEPT the one for this\n // column. That keeps the column's own options wide while the others narrow.\n const aggregationEntries = columnModels\n ?.filter(cm => cm.facetType === 'enumeration')\n .map(cm => {\n const termsAgg: TermsAggregation = { field: cm.name, size: 100 }\n if (cm.facetSortConfig?.property != null) {\n // Map FacetColumnSortProperty ('VALUE'|'FREQUENCY') → OpenSearch order key ('_key'|'_count')\n const orderKey =\n cm.facetSortConfig.property === 'VALUE' ? '_key' : '_count'\n const orderDir =\n cm.facetSortConfig.direction === 'DESC' ? 'desc' : 'asc'\n termsAgg.order = { [orderKey]: orderDir }\n }\n\n if (filterClauses.length === 0) {\n // Baseline (no selections): plain terms aggregation, no wrapping needed.\n return [cm.name, { terms: termsAgg }] as [string, Aggregation]\n }\n\n // Per-facet filter bucketing: exclude this column's own selection from the filter\n // so it stays wide, while all other active selections narrow the counts.\n const otherClauses = filterClauses.filter(clause => {\n if ('terms' in clause) return !(cm.name in clause.terms)\n if ('range' in clause) return !(cm.name in clause.range)\n // \"Not Assigned\" range clause: identify by the field inside must_not.exists.\n if ('bool' in clause)\n return clause.bool.must_not[0]?.exists?.field !== cm.name\n return true\n })\n const aggFilter: SelectionFilter =\n otherClauses.length === 0\n ? { match_all: {} }\n : otherClauses.length === 1\n ? otherClauses[0]\n : { bool: { must: otherClauses } }\n return [\n cm.name,\n {\n filter: aggFilter,\n aggregations: { [cm.name]: { terms: termsAgg } },\n } as unknown as Aggregation,\n ] as [string, Aggregation]\n })\n const aggregations =\n aggregationEntries && aggregationEntries.length > 0\n ? Object.fromEntries(aggregationEntries)\n : undefined\n\n // Map SortItem[] → OpenSearch sort shape: [{ columnName: 'asc'|'desc' }, ...]\n // filtering out entries with no direction (which mean \"remove sort\").\n const sortEntries = queryBundleRequest.query.sort\n ?.filter(\n (s): s is SortItem & { direction: 'ASC' | 'DESC' } =>\n s.direction === 'ASC' || s.direction === 'DESC',\n )\n .map(s => ({ [s.column]: s.direction.toLowerCase() }))\n\n const searchQuery: SearchSearchQuery = {\n query: query as unknown as DslQuery,\n post_filter: postFilter ? (postFilter as unknown as DslQuery) : undefined,\n aggregations,\n sort: sortEntries?.length ? sortEntries : undefined,\n size: queryBundleRequest.query.limit,\n from: queryBundleRequest.query.offset,\n }\n\n return {\n concreteType:\n 'org.sagebionetworks.repo.model.search.table.SearchIndexQuery' as const,\n searchIndexId,\n searchQuery,\n }\n}\n\n/**\n * Converts a SearchQueryResults (the actual API response) into a QueryResultBundle\n * so that existing subcomponents (FacetFilterControls, RowSetView, etc.) work unchanged.\n *\n * Mapping:\n * - totalHits → queryCount\n * - hits → queryResult.queryResults (rows derived from hit.rowId/rowVersion/fields)\n * - aggregationResults → facets (parsed from opaque OpenSearch aggregation JSON)\n */\nexport function searchQueryResultsToQueryResultBundle(\n results: SearchQueryResults | undefined,\n query: SearchIndexQuery,\n columnModels?: ColumnModel[],\n selectedFacets?: (FacetColumnValuesRequest | FacetColumnRangeRequest)[],\n): QueryResultBundle {\n if (!results) {\n return {\n concreteType: 'org.sagebionetworks.repo.model.table.QueryResultBundle',\n }\n }\n\n // Derive column names from the server-returned selectColumns. Both the row data\n // query and the metadata query request SELECT_COLUMNS, so this is always present.\n const fieldNames: string[] =\n results.selectColumns\n ?.map(col => col.name)\n .filter((n): n is string => !!n) ?? []\n\n const headers = fieldNames.map(name => ({\n name,\n columnType:\n columnModels?.find(cm => cm.name === name)?.columnType ??\n ('STRING' as const),\n }))\n\n const rows = (results.hits ?? []).map(hit => ({\n rowId: hit.rowId,\n versionNumber: hit.rowVersion,\n values: fieldNames.map(name => {\n return hit.fields?.find(f => f.name === name)?.value ?? null\n }),\n }))\n\n const queryResult =\n headers.length > 0 || rows.length > 0\n ? {\n concreteType:\n 'org.sagebionetworks.repo.model.table.QueryResult' as const,\n queryResults: {\n concreteType:\n 'org.sagebionetworks.repo.model.table.RowSet' as const,\n tableId: query.searchIndexId ?? '',\n etag: '',\n headers,\n rows,\n },\n }\n : undefined\n\n // Parse aggregationResults (opaque OpenSearch aggregation JSON) into FacetColumnResultValues.\n // Each key in aggregationResults corresponds to a column name whose aggregation was requested\n // in toSearchIndexQuery. Two response shapes are possible:\n //\n // Plain terms (no active selections):\n // { buckets: [{ key: unknown, doc_count: number }] }\n //\n // Filter-wrapped terms (per-facet filter bucketing, selections active):\n // { doc_count: number, \"<type>#columnName\": { buckets: [...] } }\n //\n // OpenSearch prefixes the aggregation type to the name in the response. The prefix depends\n // on the field type: \"sterms\" for string fields, \"lterms\" for long/integer fields, etc.\n // We detect the shape by looking for any \"type#columnName\" key with a buckets property.\n const aggregationResults = results.aggregationResults as unknown as Record<\n string,\n OpenSearchTermsAgg | Record<string, OpenSearchTermsAgg>\n > | null\n const rawFacets: FacetColumnResultValues[] = Object.entries(\n aggregationResults ?? {},\n ).map(([columnName, agg]) => {\n // Resolve the actual terms result: OpenSearch prefixes the agg type when it is\n // nested inside a filter aggregation, producing a \"sterms#columnName\" key.\n // Fall back to a direct [columnName] match (defensive), then to the agg itself\n // for the plain (no-selection) terms shape.\n const aggRecord = agg as Record<string, OpenSearchTermsAgg>\n // OpenSearch prefixes the aggregation type to the name when nested inside a filter\n // aggregation. For string fields this is \"sterms#columnName\"; for long/integer fields\n // it is \"lterms#columnName\". Rather than hardcoding known prefixes, look for any key\n // ending in \"#columnName\" so that future numeric types (e.g. double) also work.\n const nestedKey = Object.keys(aggRecord).find(key =>\n key.endsWith(`#${columnName}`),\n )\n const nested =\n (nestedKey !== undefined ? aggRecord[nestedKey] : undefined) ??\n aggRecord[columnName]\n const termsAgg: OpenSearchTermsAgg =\n nested?.buckets !== undefined ? nested : (agg as OpenSearchTermsAgg)\n const selectedValues =\n selectedFacets\n ?.filter(\n (f): f is FacetColumnValuesRequest =>\n f.concreteType ===\n FACET_COLUMN_VALUES_REQUEST_CONCRETE_TYPE_VALUE &&\n f.columnName === columnName,\n )\n .flatMap(f => f.facetValues ?? []) ?? []\n return {\n concreteType:\n 'org.sagebionetworks.repo.model.table.FacetColumnResultValues' as const,\n facetType: 'enumeration' as const,\n columnName,\n facetValues: (termsAgg.buckets ?? []).map(b => ({\n value: String(b.key),\n count: b.doc_count,\n isSelected: selectedValues.includes(String(b.key)),\n })),\n }\n })\n\n // Synthesize FacetColumnResultRange for range-type DATE/DOUBLE/INTEGER columns. The Search\n // API's aggregations only produce terms (enumeration) results; range stats are unavailable\n // server-side. columnMin/columnMax are left empty; selectedMin/selectedMax are taken\n // directly from selectedFacets so any applied range filter is still reflected in the UI.\n // Note: for INTEGER columns the RangeSlider requires real bounds, so the UI falls back to a\n // plain Range number input when columnMin/columnMax are absent.\n const syntheticRangeFacets: FacetColumnResultRange[] = (columnModels ?? [])\n .filter(\n cm =>\n cm.facetType === 'range' &&\n (cm.columnType === 'DATE' ||\n cm.columnType === 'DOUBLE' ||\n cm.columnType === 'INTEGER') &&\n !rawFacets.some(f => f.columnName === cm.name),\n )\n .map(cm => {\n const rangeFilter = selectedFacets?.find(\n (f): f is FacetColumnRangeRequest =>\n f.concreteType === FACET_COLUMN_RANGE_REQUEST_CONCRETE_TYPE_VALUE &&\n f.columnName === cm.name,\n )\n return {\n concreteType:\n 'org.sagebionetworks.repo.model.table.FacetColumnResultRange' as const,\n facetType: 'range' as const,\n columnName: cm.name,\n columnMin: '',\n columnMax: '',\n selectedMin: rangeFilter?.min,\n selectedMax: rangeFilter?.max,\n }\n })\n\n const allFacets: NonNullable<QueryResultBundle['facets']> = [\n ...(rawFacets ?? []),\n ...syntheticRangeFacets,\n ]\n\n // Reorder all facets to match the columnModels order so downstream components\n // (e.g. FacetFilterControls) render facets in the expected column order.\n const orderedFacets: QueryResultBundle['facets'] = columnModels\n ? [\n ...allFacets\n .filter(f => columnModels.some(cm => cm.name === f.columnName))\n .sort(\n (a, b) =>\n columnModels.findIndex(cm => cm.name === a.columnName) -\n columnModels.findIndex(cm => cm.name === b.columnName),\n ),\n ...allFacets.filter(\n f => !columnModels.some(cm => cm.name === f.columnName),\n ),\n ]\n : allFacets\n\n const maxScore = results.hits?.[0]?.score\n return {\n concreteType: 'org.sagebionetworks.repo.model.table.QueryResultBundle',\n queryCount: results.totalHits,\n selectColumns: headers,\n queryResult,\n facets: orderedFacets,\n ...(maxScore !== undefined && { maxScore }),\n } as QueryResultBundle\n}\n\nexport function getSearchQueryUseQueryOptions(\n queryBundleRequest: QueryBundleRequest,\n keyFactory: KeyFactory,\n accessToken: string | undefined,\n searchIndexId: string,\n columnModels?: ColumnModel[],\n searchQueryConfig?: SearchQueryConfig,\n): SearchTableQueryUseQueryOptions {\n const baseQuery: SearchIndexQuery = toSearchIndexQuery(\n queryBundleRequest,\n searchIndexId,\n columnModels,\n searchQueryConfig,\n )\n const rowDataQuery: SearchIndexQuery = {\n ...baseQuery,\n searchQuery: {\n ...baseQuery.searchQuery,\n // Omit aggregations from the row data query: facet aggregations are only needed\n // in the metadata query. Removing them here keeps the row data query key stable\n // while column models are loading, preventing an unnecessary re-fetch (and a brief\n // loading flash) when column models first arrive.\n aggregations: undefined,\n },\n // Request SELECT_COLUMNS so headers are available for row rendering.\n // HITS is returned by default even when responseParts is specified.\n responseParts: new Set(['HITS', 'SELECT_COLUMNS'] as const),\n }\n const metadataQuery: SearchIndexQuery = {\n ...baseQuery,\n searchQuery: {\n ...baseQuery.searchQuery,\n // Fetch only 1 hit: we need just the top hit's score for tab auto-selection.\n // Aggregation counts are independent of size.\n size: 1,\n },\n // Request all opt-in parts needed for the metadata response:\n // HITS → exposes the top hit's relevance score for tab auto-selection\n // TOTAL_HITS → queryCount (drives tab count / spinner resolution)\n // SELECT_COLUMNS → column headers for the table\n // Aggregation results are returned automatically when aggregations is set on the query.\n responseParts: new Set(['HITS', 'TOTAL_HITS', 'SELECT_COLUMNS'] as const),\n }\n\n // Convert responseParts Set → sorted array for query key building.\n // JSON.stringify(new Set([...])) === '{}', so two queries whose only difference is\n // their responseParts would hash to the same key and share a cache entry.\n // Using a sorted array gives each query a distinct, stable key.\n const toKeyShape = (query: SearchIndexQuery) => ({\n ...query,\n responseParts: query.responseParts\n ? [...query.responseParts].sort()\n : undefined,\n })\n\n // Fetches from the SearchQueryServicesApi and converts the SearchQueryResults responseBody\n // into QueryResultBundle shape. This allows all downstream shared components\n // (UseRowSet, QueryVisualizationWrapper, etc.) to access data.responseBody.*\n // using the same patterns as regular table queries (queryResult.queryResults, selectColumns, etc.).\n const fetchAndConvert = async (query: SearchIndexQuery) => {\n const asyncStatus = await SynapseClient.getSearchQueryAsyncJobResults(\n query,\n accessToken,\n )\n return {\n ...asyncStatus,\n responseBody: searchQueryResultsToQueryResultBundle(\n asyncStatus.responseBody,\n query,\n columnModels,\n queryBundleRequest.query.selectedFacets as\n | (FacetColumnValuesRequest | FacetColumnRangeRequest)[]\n | undefined,\n ),\n }\n }\n\n // `as unknown as` is necessary because SearchIndexQuery.responseParts is a Set but\n // toKeyShape converts it to a sorted string[] for stable cache key serialisation.\n const rowDataQueryOptions: SearchTableQueryUseQueryOptions['rowDataQueryOptions'] =\n {\n ...tableQueryUseQueryDefaults,\n queryKey: keyFactory.getSearchQueryResultWithAsyncStatusQueryKey(\n toKeyShape(rowDataQuery) as unknown as SearchIndexQuery,\n false,\n ),\n queryFn: () => fetchAndConvert(rowDataQuery),\n select: (\n data: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n ): RowSet | undefined => data.responseBody?.queryResult?.queryResults,\n }\n\n const rowDataInfiniteQueryOptions: SearchTableQueryUseQueryOptions['rowDataInfiniteQueryOptions'] =\n {\n ...tableQueryUseQueryDefaults,\n queryKey: keyFactory.getSearchQueryResultWithAsyncStatusQueryKey(\n toKeyShape(rowDataQuery) as unknown as SearchIndexQuery,\n true,\n ),\n initialPageParam: 0 as number | string | undefined,\n getNextPageParam: (\n lastPage: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n allPages: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>[],\n ): number | string | undefined => {\n return getNextPageParamForSearchQueryResults(lastPage, allPages)\n },\n queryFn: (context: { pageParam?: number | string }) => {\n const offset =\n typeof context.pageParam === 'string'\n ? parseInt(context.pageParam)\n : (context.pageParam ?? 0)\n const requestForPage: SearchIndexQuery = {\n ...rowDataQuery,\n searchQuery: {\n ...rowDataQuery.searchQuery,\n from: offset,\n },\n }\n return fetchAndConvert(requestForPage)\n },\n select: (\n data: InfiniteData<\n AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>\n >,\n ) => data,\n }\n\n const queryMetadataQueryOptions: SearchTableQueryUseQueryOptions['queryMetadataQueryOptions'] =\n {\n ...tableQueryUseQueryDefaults,\n queryKey: keyFactory.getSearchQueryResultWithAsyncStatusQueryKey(\n toKeyShape(metadataQuery) as unknown as SearchIndexQuery,\n false,\n ),\n queryFn: () => fetchAndConvert(metadataQuery),\n select: (\n data: AsynchronousJobStatus<SearchIndexQuery, QueryResultBundle>,\n ): Omit<QueryResultBundle, 'queryResult'> => {\n const result = omit(data.responseBody ?? {}, 'queryResult') as Omit<\n QueryResultBundle,\n 'queryResult'\n >\n return columnModels ? { ...result, columnModels } : result\n },\n }\n\n return {\n rowDataQueryOptions,\n rowDataInfiniteQueryOptions,\n queryMetadataQueryOptions,\n }\n}\n\n/**\n * Returns a stable callback that fetches autocomplete suggestions for a search input\n * using the synchronous `POST /search/autocomplete` endpoint.\n *\n * The query is built from the current `searchIndexId` and `autocompleteFieldName`:\n * - `searchQuery.query` is a `match_bool_prefix` clause scoped to `autocompleteFieldName`\n * - `searchQuery._source` is set to `[autocompleteFieldName]` so only that field is returned\n *\n * When `autocompleteFieldName` is undefined, the returned callback immediately returns `[]`.\n */\nexport function useGetSuggestionsForSearchIndex(\n searchIndexId: string,\n autocompleteFieldName: string | undefined,\n): (searchText: string) => Promise<string[]> {\n const { synapseClient } = useSynapseContext()\n\n return useCallback(\n async (searchText: string): Promise<string[]> => {\n if (!autocompleteFieldName) return []\n const trimmed = searchText.trim()\n if (!trimmed) return []\n const results =\n await synapseClient.searchManagementServicesClient.postRepoV1SearchAutocomplete(\n {\n searchAutocompleteRequest: {\n searchIndexId,\n searchQuery: {\n query: {\n match_bool_prefix: {\n [autocompleteFieldName]: { query: trimmed },\n },\n },\n _source: { includes: [autocompleteFieldName] },\n },\n },\n },\n )\n const suggestions: string[] = []\n for (const hit of results.hits ?? []) {\n for (const field of hit.fields ?? []) {\n if (field.value) {\n suggestions.push(field.value)\n }\n }\n }\n return suggestions\n },\n [synapseClient, searchIndexId, autocompleteFieldName],\n )\n}\n\n/**\n * Returns TanStack Query options that call the SearchQueryServicesApi (instead of the\n * standard Table Query API) but return data in the same QueryResultBundle format, allowing\n * all existing subcomponents (FacetFilterControls, RowSetView, etc.) to work unchanged.\n */\nexport function useSearchQueryUseQueryOptions(\n queryBundleRequest: QueryBundleRequest,\n searchIndexId: string,\n synapseId?: string,\n searchQueryConfig?: SearchQueryConfig,\n): SearchTableQueryUseQueryOptions {\n const { keyFactory, accessToken } = useSynapseContext()\n\n const { data: entityBundle } = useGetEntityBundle(synapseId, undefined, {\n includeTableBundle: true,\n })\n const columnModels = entityBundle?.tableBundle?.columnModels\n\n return useMemo(\n () =>\n getSearchQueryUseQueryOptions(\n queryBundleRequest,\n keyFactory,\n accessToken,\n searchIndexId,\n columnModels,\n searchQueryConfig,\n ),\n [\n keyFactory,\n accessToken,\n queryBundleRequest,\n searchIndexId,\n columnModels,\n searchQueryConfig,\n ],\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"StatisticsPlot.d.ts","sourceRoot":"","sources":["../../src/components/StatisticsPlot.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,8BAA8B,EAC/B,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAkB9C,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,6BAA6B,CAAA;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,8BAA8B,CAAA;CAC1C,CAAA;AAED,cAAM,cAAe,SAAQ,SAAS,CACpC,mBAAmB,EACnB,mBAAmB,CACpB;IACC,MAAM,CAAC,WAAW,uFAAiB;IAC3B,OAAO,EAAE,WAAW,CAAC,WAAW,CAAC,OAAO,cAAc,CAAC,CAAC,CAAA;gBACpD,KAAK,EAAE,mBAAmB;IAQ/B,iBAAiB;IAGxB;;;;OAIG;IACH,eAAe,sBAYd;IAED,QAAQ,GACN,WAAW,MAAM,EACjB,OAAO,oBAAoB,EAAE,EAC7B,aAAa,MAAM,EACnB,aAAa,MAAM;;;;;;;;;;MAyBpB;IAED,QAAQ,4DAyFP;IAEM,MAAM;CAMd;AACD,eAAe,cAAc,CAAA"}
1
+ {"version":3,"file":"StatisticsPlot.d.ts","sourceRoot":"","sources":["../../src/components/StatisticsPlot.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAC/D,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,8BAA8B,EAC/B,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAkB9C,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,6BAA6B,CAAA;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,8BAA8B,CAAA;CAC1C,CAAA;AAED,cAAM,cAAe,SAAQ,SAAS,CACpC,mBAAmB,EACnB,mBAAmB,CACpB;IACC,MAAM,CAAC,WAAW,uFAAiB;IAC3B,OAAO,EAAE,WAAW,CAAC,WAAW,CAAC,OAAO,cAAc,CAAC,CAAC,CAAA;gBACpD,KAAK,EAAE,mBAAmB;IAQ/B,iBAAiB;IAGxB;;;;OAIG;IACH,eAAe,sBAYd;IAED,QAAQ,GACN,WAAW,MAAM,EACjB,OAAO,oBAAoB,EAAE,EAC7B,aAAa,MAAM,EACnB,aAAa,MAAM;;;;;;;;;;MAyBpB;IAED,QAAQ,4DAuFP;IAEM,MAAM;CAMd;AACD,eAAe,cAAc,CAAA"}
@@ -81,7 +81,7 @@ var o = [
81
81
  xaxistype: s.toLowerCase()
82
82
  }), r && (u.yaxis = { title: r });
83
83
  let f = o ? "h" : "v", p = [];
84
- return l.fileDownloads && l.fileDownloads.months && l.fileDownloads.months.length > 0 && p.push(this.getTrace("File Downloads", l.fileDownloads.months, f, "#7CC0C4")), l.fileUploads && l.fileUploads.months && l.fileUploads.months.length > 0 && p.push(this.getTrace("File Uploads", l.fileUploads.months, f, "#D4689A")), p.length > 0 ? /* @__PURE__ */ a(n, {
84
+ return l.fileDownloads?.months && l.fileDownloads.months.length > 0 && p.push(this.getTrace("File Downloads", l.fileDownloads.months, f, "#7CC0C4")), l.fileUploads?.months && l.fileUploads.months.length > 0 && p.push(this.getTrace("File Uploads", l.fileUploads.months, f, "#D4689A")), p.length > 0 ? /* @__PURE__ */ a(n, {
85
85
  layout: u,
86
86
  data: p,
87
87
  config: d,
@@ -1 +1 @@
1
- {"version":3,"file":"StatisticsPlot.js","names":[],"sources":["../../src/components/StatisticsPlot.tsx"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { SynapseContext } from '@/utils/context/SynapseContext'\nimport {\n FilesCountStatistics,\n ProjectFilesStatisticsRequest,\n ProjectFilesStatisticsResponse,\n} from '@sage-bionetworks/synapse-types'\nimport { Component, ContextType } from 'react'\nimport Plot from './Plot/Plot'\n\nconst months = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n]\n\nexport type StatisticsPlotProps = {\n request: ProjectFilesStatisticsRequest\n title?: string\n xtitle?: string\n ytitle?: string\n isHorizontal?: boolean\n xaxistype?: string\n showlegend?: boolean\n}\n\ntype StatisticsPlotState = {\n isLoaded: boolean\n plotData?: ProjectFilesStatisticsResponse\n}\n\nclass StatisticsPlot extends Component<\n StatisticsPlotProps,\n StatisticsPlotState\n> {\n static contextType = SynapseContext\n declare context: NonNullable<ContextType<typeof SynapseContext>>\n constructor(props: StatisticsPlotProps) {\n super(props)\n this.state = {\n isLoaded: false,\n plotData: {} as ProjectFilesStatisticsResponse,\n }\n }\n\n public componentDidMount() {\n this.fetchPlotlyData()\n }\n /**\n * Get data for plotly\n *\n * @returns data corresponding to plotly widget\n */\n fetchPlotlyData = async () => {\n const { request } = this.props\n return SynapseClient.getProjectStatistics(request, this.context.accessToken)\n .then((data: ProjectFilesStatisticsResponse) => {\n this.setState({\n isLoaded: true,\n plotData: data,\n })\n })\n .catch((err: any) => {\n console.log('Error on call to get statistics ', err)\n })\n }\n\n getTrace = (\n traceName: string,\n stats: FilesCountStatistics[],\n orientation: string,\n markerColor: string,\n ) => {\n const x: string[] = []\n const y: number[] = []\n const trace = {\n orientation,\n x,\n y,\n name: traceName,\n type: 'bar',\n marker: { color: markerColor },\n hovertemplate:\n // see S3 Formatting options: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format\n `%{y:n} <br><extra>${traceName}</extra>`,\n }\n for (const statPoint of stats) {\n const statPointDate: Date = new Date(statPoint.rangeStart)\n trace.x.push(\n `${\n months[statPointDate.getUTCMonth()]\n } ${statPointDate.getUTCFullYear()}`,\n )\n trace.y.push(statPoint.filesCount)\n }\n return trace\n }\n\n showPlot = () => {\n if (!this.state.isLoaded || !this.state.plotData) {\n return\n }\n const { title, xtitle, ytitle, isHorizontal, xaxistype, showlegend } =\n this.props\n const plotData = this.state.plotData\n const layout: any = {\n showlegend,\n title,\n barmode: 'stack',\n hovermode: 'x',\n }\n const config: any = {\n displayModeBar: true,\n displaylogo: false,\n // options found here: https://github.com/plotly/plotly.js/blob/master/src/components/modebar/buttons.js\n modeBarButtonsToRemove: [\n 'sendDataToCloud',\n 'hoverCompareCartesian',\n 'select2d',\n 'lasso2d',\n 'zoom2d',\n 'resetScale2d',\n 'hoverClosestCartesian',\n 'toggleSpikelines',\n ],\n }\n if (xtitle) {\n layout.xaxis = {\n title: xtitle,\n }\n }\n if (xaxistype) {\n layout.xaxis = {\n ...layout.xaxis,\n xaxistype: xaxistype.toLowerCase(),\n }\n }\n\n if (ytitle) {\n layout.yaxis = {\n title: ytitle,\n }\n }\n // init plot_data\n const orientation: string = isHorizontal ? 'h' : 'v'\n const traces: any = []\n if (\n plotData.fileDownloads &&\n plotData.fileDownloads.months &&\n plotData.fileDownloads.months.length > 0\n ) {\n // add file downloads trace\n traces.push(\n this.getTrace(\n 'File Downloads',\n plotData.fileDownloads.months,\n orientation,\n '#7CC0C4',\n ),\n )\n }\n if (\n plotData.fileUploads &&\n plotData.fileUploads.months &&\n plotData.fileUploads.months.length > 0\n ) {\n // add file uploads trace\n traces.push(\n this.getTrace(\n 'File Uploads',\n plotData.fileUploads.months,\n orientation,\n '#D4689A',\n ),\n )\n }\n if (traces.length > 0)\n return (\n <Plot\n layout={layout}\n data={traces}\n config={config}\n className=\"SRC-fullWidth\"\n useResizeHandler={true}\n />\n )\n else return <></>\n }\n\n public render() {\n if (!this.state.isLoaded) {\n return null\n }\n return this.showPlot()\n }\n}\nexport default StatisticsPlot\n"],"mappings":";;;;;;AAUA,IAAM,IAAS;CACb;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,GAiBM,IAAN,cAA6B,EAG3B;CACA,OAAO,cAAc;CAErB,YAAY,GAA4B;EAEtC,AADA,MAAM,CAAK,GACX,KAAK,QAAQ;GACX,UAAU;GACV,UAAU,CAAC;EACb;CACF;CAEA,oBAA2B;EACzB,KAAK,gBAAgB;CACvB;CAMA,kBAAkB,YAAY;EAC5B,IAAM,EAAE,eAAY,KAAK;EACzB,OAAO,EAAc,qBAAqB,GAAS,KAAK,QAAQ,WAAW,EACxE,MAAM,MAAyC;GAC9C,KAAK,SAAS;IACZ,UAAU;IACV,UAAU;GACZ,CAAC;EACH,CAAC,EACA,OAAO,MAAa;GACnB,QAAQ,IAAI,oCAAoC,CAAG;EACrD,CAAC;CACL;CAEA,YACE,GACA,GACA,GACA,MACG;EAGH,IAAM,IAAQ;GACZ;GACA,GAAA,CAAA;GACA,GAAA,CAAA;GACA,MAAM;GACN,MAAM;GACN,QAAQ,EAAE,OAAO,EAAY;GAC7B,eAEE,qBAAqB,EAAU;EACnC;EACA,KAAK,IAAM,KAAa,GAAO;GAC7B,IAAM,IAAsB,IAAI,KAAK,EAAU,UAAU;GAMzD,AALA,EAAM,EAAE,KACN,GACE,EAAO,EAAc,YAAY,GAClC,GAAG,EAAc,eAAe,GACnC,GACA,EAAM,EAAE,KAAK,EAAU,UAAU;EACnC;EACA,OAAO;CACT;CAEA,iBAAiB;EACf,IAAI,CAAC,KAAK,MAAM,YAAY,CAAC,KAAK,MAAM,UACtC;EAEF,IAAM,EAAE,UAAO,WAAQ,WAAQ,iBAAc,cAAW,kBACtD,KAAK,OACD,IAAW,KAAK,MAAM,UACtB,IAAc;GAClB;GACA;GACA,SAAS;GACT,WAAW;EACb,GACM,IAAc;GAClB,gBAAgB;GAChB,aAAa;GAEb,wBAAwB;IACtB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF;EACF;EAaA,AAZI,MACF,EAAO,QAAQ,EACb,OAAO,EACT,IAEE,MACF,EAAO,QAAQ;GACb,GAAG,EAAO;GACV,WAAW,EAAU,YAAY;EACnC,IAGE,MACF,EAAO,QAAQ,EACb,OAAO,EACT;EAGF,IAAM,IAAsB,IAAe,MAAM,KAC3C,IAAc,CAAC;EAyChB,OAvCH,EAAS,iBACT,EAAS,cAAc,UACvB,EAAS,cAAc,OAAO,SAAS,KAGvC,EAAO,KACL,KAAK,SACH,kBACA,EAAS,cAAc,QACvB,GACA,SACF,CACF,GAGA,EAAS,eACT,EAAS,YAAY,UACrB,EAAS,YAAY,OAAO,SAAS,KAGrC,EAAO,KACL,KAAK,SACH,gBACA,EAAS,YAAY,QACrB,GACA,SACF,CACF,GAEE,EAAO,SAAS,IAEhB,kBAAC,GAAD;GACU;GACR,MAAM;GACE;GACR,WAAU;GACV,kBAAkB;EACnB,CAAA,IAEO,kBAAA,GAAA,CAAI,CAAA;CAClB;CAEA,SAAgB;EAId,OAHK,KAAK,MAAM,WAGT,KAAK,SAAS,IAFZ;CAGX;AACF"}
1
+ {"version":3,"file":"StatisticsPlot.js","names":[],"sources":["../../src/components/StatisticsPlot.tsx"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { SynapseContext } from '@/utils/context/SynapseContext'\nimport {\n FilesCountStatistics,\n ProjectFilesStatisticsRequest,\n ProjectFilesStatisticsResponse,\n} from '@sage-bionetworks/synapse-types'\nimport { Component, ContextType } from 'react'\nimport Plot from './Plot/Plot'\n\nconst months = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n]\n\nexport type StatisticsPlotProps = {\n request: ProjectFilesStatisticsRequest\n title?: string\n xtitle?: string\n ytitle?: string\n isHorizontal?: boolean\n xaxistype?: string\n showlegend?: boolean\n}\n\ntype StatisticsPlotState = {\n isLoaded: boolean\n plotData?: ProjectFilesStatisticsResponse\n}\n\nclass StatisticsPlot extends Component<\n StatisticsPlotProps,\n StatisticsPlotState\n> {\n static contextType = SynapseContext\n declare context: NonNullable<ContextType<typeof SynapseContext>>\n constructor(props: StatisticsPlotProps) {\n super(props)\n this.state = {\n isLoaded: false,\n plotData: {} as ProjectFilesStatisticsResponse,\n }\n }\n\n public componentDidMount() {\n this.fetchPlotlyData()\n }\n /**\n * Get data for plotly\n *\n * @returns data corresponding to plotly widget\n */\n fetchPlotlyData = async () => {\n const { request } = this.props\n return SynapseClient.getProjectStatistics(request, this.context.accessToken)\n .then((data: ProjectFilesStatisticsResponse) => {\n this.setState({\n isLoaded: true,\n plotData: data,\n })\n })\n .catch((err: any) => {\n console.log('Error on call to get statistics ', err)\n })\n }\n\n getTrace = (\n traceName: string,\n stats: FilesCountStatistics[],\n orientation: string,\n markerColor: string,\n ) => {\n const x: string[] = []\n const y: number[] = []\n const trace = {\n orientation,\n x,\n y,\n name: traceName,\n type: 'bar',\n marker: { color: markerColor },\n hovertemplate:\n // see S3 Formatting options: https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format\n `%{y:n} <br><extra>${traceName}</extra>`,\n }\n for (const statPoint of stats) {\n const statPointDate: Date = new Date(statPoint.rangeStart)\n trace.x.push(\n `${\n months[statPointDate.getUTCMonth()]\n } ${statPointDate.getUTCFullYear()}`,\n )\n trace.y.push(statPoint.filesCount)\n }\n return trace\n }\n\n showPlot = () => {\n if (!this.state.isLoaded || !this.state.plotData) {\n return\n }\n const { title, xtitle, ytitle, isHorizontal, xaxistype, showlegend } =\n this.props\n const plotData = this.state.plotData\n const layout: any = {\n showlegend,\n title,\n barmode: 'stack',\n hovermode: 'x',\n }\n const config: any = {\n displayModeBar: true,\n displaylogo: false,\n // options found here: https://github.com/plotly/plotly.js/blob/master/src/components/modebar/buttons.js\n modeBarButtonsToRemove: [\n 'sendDataToCloud',\n 'hoverCompareCartesian',\n 'select2d',\n 'lasso2d',\n 'zoom2d',\n 'resetScale2d',\n 'hoverClosestCartesian',\n 'toggleSpikelines',\n ],\n }\n if (xtitle) {\n layout.xaxis = {\n title: xtitle,\n }\n }\n if (xaxistype) {\n layout.xaxis = {\n ...layout.xaxis,\n xaxistype: xaxistype.toLowerCase(),\n }\n }\n\n if (ytitle) {\n layout.yaxis = {\n title: ytitle,\n }\n }\n // init plot_data\n const orientation: string = isHorizontal ? 'h' : 'v'\n const traces: any = []\n if (\n plotData.fileDownloads?.months &&\n plotData.fileDownloads.months.length > 0\n ) {\n // add file downloads trace\n traces.push(\n this.getTrace(\n 'File Downloads',\n plotData.fileDownloads.months,\n orientation,\n '#7CC0C4',\n ),\n )\n }\n if (\n plotData.fileUploads?.months &&\n plotData.fileUploads.months.length > 0\n ) {\n // add file uploads trace\n traces.push(\n this.getTrace(\n 'File Uploads',\n plotData.fileUploads.months,\n orientation,\n '#D4689A',\n ),\n )\n }\n if (traces.length > 0)\n return (\n <Plot\n layout={layout}\n data={traces}\n config={config}\n className=\"SRC-fullWidth\"\n useResizeHandler={true}\n />\n )\n else return <></>\n }\n\n public render() {\n if (!this.state.isLoaded) {\n return null\n }\n return this.showPlot()\n }\n}\nexport default StatisticsPlot\n"],"mappings":";;;;;;AAUA,IAAM,IAAS;CACb;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,GAiBM,IAAN,cAA6B,EAG3B;CACA,OAAO,cAAc;CAErB,YAAY,GAA4B;EAEtC,AADA,MAAM,CAAK,GACX,KAAK,QAAQ;GACX,UAAU;GACV,UAAU,CAAC;EACb;CACF;CAEA,oBAA2B;EACzB,KAAK,gBAAgB;CACvB;CAMA,kBAAkB,YAAY;EAC5B,IAAM,EAAE,eAAY,KAAK;EACzB,OAAO,EAAc,qBAAqB,GAAS,KAAK,QAAQ,WAAW,EACxE,MAAM,MAAyC;GAC9C,KAAK,SAAS;IACZ,UAAU;IACV,UAAU;GACZ,CAAC;EACH,CAAC,EACA,OAAO,MAAa;GACnB,QAAQ,IAAI,oCAAoC,CAAG;EACrD,CAAC;CACL;CAEA,YACE,GACA,GACA,GACA,MACG;EAGH,IAAM,IAAQ;GACZ;GACA,GAAA,CAAA;GACA,GAAA,CAAA;GACA,MAAM;GACN,MAAM;GACN,QAAQ,EAAE,OAAO,EAAY;GAC7B,eAEE,qBAAqB,EAAU;EACnC;EACA,KAAK,IAAM,KAAa,GAAO;GAC7B,IAAM,IAAsB,IAAI,KAAK,EAAU,UAAU;GAMzD,AALA,EAAM,EAAE,KACN,GACE,EAAO,EAAc,YAAY,GAClC,GAAG,EAAc,eAAe,GACnC,GACA,EAAM,EAAE,KAAK,EAAU,UAAU;EACnC;EACA,OAAO;CACT;CAEA,iBAAiB;EACf,IAAI,CAAC,KAAK,MAAM,YAAY,CAAC,KAAK,MAAM,UACtC;EAEF,IAAM,EAAE,UAAO,WAAQ,WAAQ,iBAAc,cAAW,kBACtD,KAAK,OACD,IAAW,KAAK,MAAM,UACtB,IAAc;GAClB;GACA;GACA,SAAS;GACT,WAAW;EACb,GACM,IAAc;GAClB,gBAAgB;GAChB,aAAa;GAEb,wBAAwB;IACtB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF;EACF;EAaA,AAZI,MACF,EAAO,QAAQ,EACb,OAAO,EACT,IAEE,MACF,EAAO,QAAQ;GACb,GAAG,EAAO;GACV,WAAW,EAAU,YAAY;EACnC,IAGE,MACF,EAAO,QAAQ,EACb,OAAO,EACT;EAGF,IAAM,IAAsB,IAAe,MAAM,KAC3C,IAAc,CAAC;EAuChB,OArCH,EAAS,eAAe,UACxB,EAAS,cAAc,OAAO,SAAS,KAGvC,EAAO,KACL,KAAK,SACH,kBACA,EAAS,cAAc,QACvB,GACA,SACF,CACF,GAGA,EAAS,aAAa,UACtB,EAAS,YAAY,OAAO,SAAS,KAGrC,EAAO,KACL,KAAK,SACH,gBACA,EAAS,YAAY,QACrB,GACA,SACF,CACF,GAEE,EAAO,SAAS,IAEhB,kBAAC,GAAD;GACU;GACR,MAAM;GACE;GACR,WAAU;GACV,kBAAkB;EACnB,CAAA,IAEO,kBAAA,GAAA,CAAI,CAAA;CAClB;CAEA,SAAgB;EAId,OAHK,KAAK,MAAM,WAGT,KAAK,SAAS,IAFZ;CAGX;AACF"}
@@ -37,7 +37,7 @@ var h = class extends s {
37
37
  let n = {}, r = this.props.dataFileHandleId, i = this.props.submitted, a, s, c;
38
38
  if (r) {
39
39
  let o = await t.getFileHandleContentFromID(r, e);
40
- n = JSON.parse(o), i && n && n.metadata && ({formSchemaVersion: a, uiSchemaVersion: s, navSchemaVersion: c} = n.metadata);
40
+ n = JSON.parse(o), i && n?.metadata && ({formSchemaVersion: a, uiSchemaVersion: s, navSchemaVersion: c} = n.metadata);
41
41
  }
42
42
  let l = [
43
43
  this.getFileEntityDataDereferenced(e, this.props.formSchemaEntityId, a),
@@ -1 +1 @@
1
- {"version":3,"file":"SynapseFormWrapper.js","names":[],"sources":["../../../src/components/SynapseForm/SynapseFormWrapper.tsx"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { useSynapseContext } from '@/utils'\nimport { SRC_SIGN_IN_CLASS } from '@/utils/SynapseConstants'\nimport $RefParser from '@apidevtools/json-schema-ref-parser'\nimport { Button } from '@mui/material'\nimport { UiSchema } from '@rjsf/utils'\nimport { FormData } from '@sage-bionetworks/synapse-types'\nimport { get, includes } from 'lodash-es'\nimport { Component } from 'react'\nimport FullWidthAlert from '../FullWidthAlert'\nimport SynapseForm from './SynapseForm'\nimport { getFileEntityData } from './SynapseFormUtils'\nimport { StatusEnum } from './types'\n\n/**\n * TODO: SWC-5612 - Replace token prop with SynapseContext.accessToken\n *\n * This wasn't done because Enzyme's shallow renderer is not currently\n * compatible with the `contextType` field in the React 16+ context API.\n *\n * This can be fixed by rewriting tests to not rely on the shallow renderer.\n *\n * See here: https://github.com/enzymejs/enzyme/issues/1553\n */\n\nexport type SynapseFormWrapperProps = {\n // Provide the parent container (folder/project), that should contain a folder (named <user_id>) that this user can write to.\n formSchemaEntityId: string // Synapse file that contains the form schema.\n formUiSchemaEntityId: string // Synapse file that contains the form ui schema.\n formNavSchemaEntityId: string //Synapse file that consists screen nav schema\n token?: string // user's access token\n isWizardMode?: boolean // if we are displaying the form in wizard mode\n fileNamePath: string // path in data to specify the name of saved file\n formTitle: string //for UI customization\n formClass?: string // to support potential theming\n\n formDataId?: string //formDataId for user data form data\n dataFileHandleId?: string //fileHandle to get userData\n submitted?: boolean // if the file has been submitted\n formGroupId: string\n}\n\ntype SynapseFormWrapperState = {\n notification?: Notification\n isLoading?: boolean\n formDataId?: string // file holding user form data\n formData?: any // form data that prepopulates the form\n formSchema?: any // schema that drives the form\n formUiSchema?: UiSchema // ui schema that directs how to render the form elements\n formNavSchema?: any // drives the steps left panel\n\n status?: StatusEnum\n}\n\ntype Error = {\n name?: string\n message?: string\n}\n\ninterface Notification extends Error {\n type: StatusEnum\n}\n\nclass _SynapseFormWrapper extends Component<\n SynapseFormWrapperProps,\n SynapseFormWrapperState\n> {\n constructor(props: SynapseFormWrapperProps) {\n super(props)\n this.state = {\n isLoading: true,\n formDataId: this.props?.formDataId,\n }\n }\n\n componentDidMount() {\n this.getData(this.props.token)\n }\n\n componentDidUpdate(prevProps: SynapseFormWrapperProps) {\n const shouldUpdate = this.props.token !== prevProps.token\n if (shouldUpdate) {\n this.getData(this.props.token)\n }\n }\n\n //same as above but also uses $RefParser to convert json $refs to regular json\n getFileEntityDataDereferenced = async (\n token: string,\n entityId: string,\n versionNumber?: string,\n ): Promise<{ version?: number; content: JSON }> => {\n const { version, content } = await getFileEntityData(\n token,\n entityId,\n versionNumber,\n this.onError,\n )\n const derefContent = await $RefParser.dereference<JSON>(content)\n return {\n version: version,\n content: derefContent,\n }\n }\n\n getData = async (token?: string): Promise<void> => {\n if (!token) {\n return\n }\n try {\n let formData: Record<string, any> = {}\n const dataFileHandleId = this.props.dataFileHandleId\n const submitted = this.props.submitted\n let formSchemaVersion = undefined\n let uiSchemaVersion = undefined\n let navSchemaVersion = undefined\n\n //for not new form we need to get the data\n //and if it is submitted form we need to pull appropriate schema versions\n //for new form (no dataFileHandleId) we need to populate schema versions\n\n if (dataFileHandleId) {\n const fileData = await SynapseClient.getFileHandleContentFromID(\n dataFileHandleId,\n token,\n )\n formData = JSON.parse(fileData)\n if (submitted && formData && formData['metadata']) {\n ;({ formSchemaVersion, uiSchemaVersion, navSchemaVersion } =\n formData['metadata'])\n }\n }\n\n const promises = [\n this.getFileEntityDataDereferenced(\n token,\n this.props.formSchemaEntityId,\n formSchemaVersion,\n ),\n getFileEntityData(\n token,\n this.props.formUiSchemaEntityId,\n uiSchemaVersion,\n this.onError,\n ),\n getFileEntityData(\n token,\n this.props.formNavSchemaEntityId,\n navSchemaVersion,\n this.onError,\n ),\n ]\n const configData = await Promise.all(promises)\n\n if (!dataFileHandleId) {\n //if we are creating a new file - store the versions\n formData = {\n metadata: {\n formSchemaVersion: configData[0].version,\n uiSchemaVersion: configData[1].version,\n navSchemaVersion: configData[2].version,\n },\n }\n }\n this.setState({\n formData: formData,\n formSchema: configData[0].content,\n formUiSchema: configData[1].content as unknown as UiSchema,\n formNavSchema: configData[2].content,\n isLoading: false,\n })\n } catch (error) {\n this.onError({ message: error })\n } finally {\n this.setState({\n isLoading: false,\n })\n }\n }\n\n finishedProcessing = (status: StatusEnum, message?: string) => {\n this.setState({\n isLoading: false,\n notification: { type: status, message: message },\n status: status,\n })\n //this will show the update message for 7 seconds\n setTimeout(() => {\n this.setState({ status: undefined })\n }, 7000)\n }\n\n onError = (error: Error) => {\n this.setState({\n notification: {\n type: StatusEnum.ERROR,\n message: error.message,\n name: error.name,\n },\n status: StatusEnum.ERROR,\n isLoading: false,\n })\n }\n\n submitForm = async (formData: any) => {\n await this.saveToFile(formData)\n\n this.setState({\n isLoading: true,\n })\n\n await SynapseClient.submitFormData(this.state.formDataId!, this.props.token)\n this.finishedProcessing(StatusEnum.SUBMIT_SUCCESS, 'File Submitted')\n }\n\n createOrUpdateFormDataFile = async (\n fileName: string,\n fileContentsBlob: Blob,\n ): Promise<FormData> => {\n fileName = `${fileName}.json`\n const fileUploadComplete = await SynapseClient.uploadFile(\n this.props.token,\n fileName,\n fileContentsBlob,\n )\n const formGroupId = this.props.formGroupId\n if (!formGroupId) {\n console.error('formGroupId must be defined')\n throw new Error('formGroupId must be defined')\n }\n try {\n // do we need to create a new file entity, or update an existing file entity?\n const newFileHandleId = fileUploadComplete.fileHandleId\n\n let formData\n if (this.state.formDataId) {\n formData = await SynapseClient.updateFormData(\n this.state.formDataId,\n fileName,\n newFileHandleId,\n this.props.token!,\n )\n } else {\n formData = await SynapseClient.createFormData(\n formGroupId,\n fileName,\n newFileHandleId,\n this.props.token!,\n )\n }\n\n return formData\n } catch (error) {\n this.onError(error)\n throw error\n }\n }\n\n saveToFile = async (data: any) => {\n const fileName = get(data, this.props.fileNamePath)\n this.setState({\n status: StatusEnum.PROGRESS,\n notification: { type: StatusEnum.PROGRESS, message: 'Progress' },\n isLoading: true,\n })\n\n if (!fileName) {\n // try to find corresponding property. By convention it should be first level property\n // and follow pattern screen.prop\n let errorTitle = 'File Name'\n try {\n // get it to the schema format\n const searchString = `${this.props.fileNamePath.replace(\n '.',\n '.properties.',\n )}.title`\n errorTitle = get(\n this.state.formSchema.properties,\n searchString,\n errorTitle,\n )\n } finally {\n const error = new Error(\n `Please Provide the ${errorTitle} before saving`,\n )\n this.onError(error)\n }\n return\n }\n\n try {\n const fileContent: Blob = new Blob([JSON.stringify(data)], {\n type: 'text/json',\n })\n\n const formData = await this.createOrUpdateFormDataFile(\n fileName,\n fileContent,\n )\n this.setState({\n formDataId: formData.formDataId,\n })\n\n this.finishedProcessing(StatusEnum.SAVE_SUCCESS, 'File Saved')\n } catch (error) {\n this.onError(error)\n }\n }\n\n isReadyToDisplayForm = (state: SynapseFormWrapperState): boolean => {\n return (\n this.state.status !== StatusEnum.ERROR_CRITICAL &&\n state.formSchema &&\n state.formUiSchema &&\n state.formNavSchema &&\n state.formData\n )\n }\n\n renderLoader = (\n state: SynapseFormWrapperState,\n props: SynapseFormWrapperProps,\n ) => {\n if (\n includes([StatusEnum.ERROR, StatusEnum.ERROR_CRITICAL], state.status) &&\n props.token &&\n state.isLoading\n ) {\n return (\n <div className=\"text-center\">\n <span className={'spinner'} />\n </div>\n )\n } else {\n return <></>\n }\n }\n\n renderNotification = (notification?: Notification) => {\n if (!notification) {\n return <></>\n }\n if (notification.type === StatusEnum.ERROR) {\n return (\n <FullWidthAlert\n variant=\"danger\"\n title=\"Error\"\n isGlobal={false}\n description={\n <>\n {notification.name} {notification.message}\n </>\n }\n onClose={() => this.setState({ status: undefined })}\n />\n )\n }\n\n return <></>\n }\n\n renderUnauthenticatedView = (token: string | undefined) => {\n if (token) {\n return <></>\n } else {\n return (\n <div className=\"panel padding-full unauthenticated text-center\">\n Please\n <Button\n variant=\"text\"\n color=\"primary\"\n sx={{\n mx: 1,\n fontSize: '1.4rem',\n verticalAlign: 'baseline',\n textTransform: 'none',\n }}\n className={`${SRC_SIGN_IN_CLASS}`}\n >\n sign in\n </Button>\n to initiate or continue your submission\n </div>\n )\n }\n }\n\n render() {\n return (\n <div className={`theme-${this.props.formClass}`}>\n <div className=\"SRC-ReactJsonForm\">\n {this.renderNotification(this.state.notification)}\n {this.renderLoader(this.state, this.props)}\n {this.renderUnauthenticatedView(this.props.token)}\n\n {this.isReadyToDisplayForm(this.state) && (\n <div>\n <SynapseForm\n schema={this.state.formSchema}\n uiSchema={this.state.formUiSchema!}\n formData={this.state.formData}\n navSchema={this.state.formNavSchema}\n isWizardMode={this.props.isWizardMode}\n formTitle={this.props.formTitle}\n formClass={this.props.formClass}\n callbackStatus={this.state.status}\n onSave={(data: any) => {\n this.saveToFile(data)\n }}\n onSubmit={(data: any) => {\n this.submitForm(data)\n }}\n isSubmitted={this.props.submitted}\n />\n </div>\n )}\n </div>\n </div>\n )\n }\n}\n\nexport function SynapseFormWrapper(props: SynapseFormWrapperProps) {\n const synapseContext = useSynapseContext()\n\n return <_SynapseFormWrapper {...props} token={synapseContext.accessToken} />\n}\n\nexport default SynapseFormWrapper\n"],"mappings":";;;;;;;;;;;;;;AA+DA,IAAM,IAAN,cAAkC,EAGhC;CACA,YAAY,GAAgC;EAE1C,AADA,MAAM,CAAK,GACX,KAAK,QAAQ;GACX,WAAW;GACX,YAAY,KAAK,OAAO;EAC1B;CACF;CAEA,oBAAoB;EAClB,KAAK,QAAQ,KAAK,MAAM,KAAK;CAC/B;CAEA,mBAAmB,GAAoC;EAErD,AADqB,KAAK,MAAM,UAAU,EAAU,SAElD,KAAK,QAAQ,KAAK,MAAM,KAAK;CAEjC;CAGA,gCAAgC,OAC9B,GACA,GACA,MACiD;EACjD,IAAM,EAAE,YAAS,eAAY,MAAM,EACjC,GACA,GACA,GACA,KAAK,OACP;EAEA,OAAO;GACI;GACT,SAAS,MAHgB,EAAW,YAAkB,CAAO;EAI/D;CACF;CAEA,UAAU,OAAO,MAAkC;EAC5C,OAGL,IAAI;GACF,IAAI,IAAgC,CAAC,GAC/B,IAAmB,KAAK,MAAM,kBAC9B,IAAY,KAAK,MAAM,WACzB,GACA,GACA;GAMJ,IAAI,GAAkB;IACpB,IAAM,IAAW,MAAM,EAAc,2BACnC,GACA,CACF;IAEA,AADA,IAAW,KAAK,MAAM,CAAQ,GAC1B,KAAa,KAAY,EAAS,aAClC,kEACA,EAAS;GAEf;GAEA,IAAM,IAAW;IACf,KAAK,8BACH,GACA,KAAK,MAAM,oBACX,CACF;IACA,EACE,GACA,KAAK,MAAM,sBACX,GACA,KAAK,OACP;IACA,EACE,GACA,KAAK,MAAM,uBACX,GACA,KAAK,OACP;GACF,GACM,IAAa,MAAM,QAAQ,IAAI,CAAQ;GAY7C,AAVK,MAEH,IAAW,EACT,UAAU;IACR,mBAAmB,EAAW,GAAG;IACjC,iBAAiB,EAAW,GAAG;IAC/B,kBAAkB,EAAW,GAAG;GAClC,EACF,IAEF,KAAK,SAAS;IACF;IACV,YAAY,EAAW,GAAG;IAC1B,cAAc,EAAW,GAAG;IAC5B,eAAe,EAAW,GAAG;IAC7B,WAAW;GACb,CAAC;EACH,SAAS,GAAO;GACd,KAAK,QAAQ,EAAE,SAAS,EAAM,CAAC;EACjC,UAAU;GACR,KAAK,SAAS,EACZ,WAAW,GACb,CAAC;EACH;CACF;CAEA,sBAAsB,GAAoB,MAAqB;EAO7D,AANA,KAAK,SAAS;GACZ,WAAW;GACX,cAAc;IAAE,MAAM;IAAiB;GAAQ;GACvC;EACV,CAAC,GAED,iBAAiB;GACf,KAAK,SAAS,EAAE,QAAQ,KAAA,EAAU,CAAC;EACrC,GAAG,GAAI;CACT;CAEA,WAAW,MAAiB;EAC1B,KAAK,SAAS;GACZ,cAAc;IACZ,MAAM,EAAW;IACjB,SAAS,EAAM;IACf,MAAM,EAAM;GACd;GACA,QAAQ,EAAW;GACnB,WAAW;EACb,CAAC;CACH;CAEA,aAAa,OAAO,MAAkB;EAQpC,AAPA,MAAM,KAAK,WAAW,CAAQ,GAE9B,KAAK,SAAS,EACZ,WAAW,GACb,CAAC,GAED,MAAM,EAAc,eAAe,KAAK,MAAM,YAAa,KAAK,MAAM,KAAK,GAC3E,KAAK,mBAAmB,EAAW,gBAAgB,gBAAgB;CACrE;CAEA,6BAA6B,OAC3B,GACA,MACsB;EACtB,IAAW,GAAG,EAAS;EACvB,IAAM,IAAqB,MAAM,EAAc,WAC7C,KAAK,MAAM,OACX,GACA,CACF,GACM,IAAc,KAAK,MAAM;EAC/B,IAAI,CAAC,GAEH,MADA,QAAQ,MAAM,6BAA6B,GACjC,MAAM,6BAA6B;EAE/C,IAAI;GAEF,IAAM,IAAkB,EAAmB,cAEvC;GAiBJ,OAhBA,AAQE,IARE,KAAK,MAAM,aACF,MAAM,EAAc,eAC7B,KAAK,MAAM,YACX,GACA,GACA,KAAK,MAAM,KACb,IAEW,MAAM,EAAc,eAC7B,GACA,GACA,GACA,KAAK,MAAM,KACb,GAGK;EACT,SAAS,GAAO;GAEd,MADA,KAAK,QAAQ,CAAK,GACZ;EACR;CACF;CAEA,aAAa,OAAO,MAAc;EAChC,IAAM,IAAW,EAAI,GAAM,KAAK,MAAM,YAAY;EAOlD,IANA,KAAK,SAAS;GACZ,QAAQ,EAAW;GACnB,cAAc;IAAE,MAAM,EAAW;IAAU,SAAS;GAAW;GAC/D,WAAW;EACb,CAAC,GAEG,CAAC,GAAU;GAGb,IAAI,IAAa;GACjB,IAAI;IAEF,IAAM,IAAe,GAAG,KAAK,MAAM,aAAa,QAC9C,KACA,cACF,EAAE;IACF,IAAa,EACX,KAAK,MAAM,WAAW,YACtB,GACA,CACF;GACF,UAAU;IACR,IAAM,IAAQ,gBAAI,MAChB,sBAAsB,EAAW,eACnC;IACA,KAAK,QAAQ,CAAK;GACpB;GACA;EACF;EAEA,IAAI;GACF,IAAM,IAAoB,IAAI,KAAK,CAAC,KAAK,UAAU,CAAI,CAAC,GAAG,EACzD,MAAM,YACR,CAAC,GAEK,IAAW,MAAM,KAAK,2BAC1B,GACA,CACF;GAKA,AAJA,KAAK,SAAS,EACZ,YAAY,EAAS,WACvB,CAAC,GAED,KAAK,mBAAmB,EAAW,cAAc,YAAY;EAC/D,SAAS,GAAO;GACd,KAAK,QAAQ,CAAK;EACpB;CACF;CAEA,wBAAwB,MAEpB,KAAK,MAAM,WAAW,EAAW,kBACjC,EAAM,cACN,EAAM,gBACN,EAAM,iBACN,EAAM;CAIV,gBACE,GACA,MAGE,EAAS,CAAC,EAAW,OAAO,EAAW,cAAc,GAAG,EAAM,MAAM,KACpE,EAAM,SACN,EAAM,YAGJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,QAAD,EAAM,WAAW,UAAY,CAAA;CAC1B,CAAA,IAGA,kBAAA,GAAA,CAAI,CAAA;CAIf,sBAAsB,MACf,KAGD,EAAa,SAAS,EAAW,QAEjC,kBAAC,GAAD;EACE,SAAQ;EACR,OAAM;EACN,UAAU;EACV,aACE,kBAAA,GAAA,EAAA,UAAA;GACG,EAAa;GAAK;GAAE,EAAa;EAClC,EAAA,CAAA;EAEJ,eAAe,KAAK,SAAS,EAAE,QAAQ,KAAA,EAAU,CAAC;CACnD,CAAA,IAIE,kBAAA,GAAA,CAAI,CAAA;CAGb,6BAA6B,MACvB,IACK,kBAAA,GAAA,CAAI,CAAA,IAGT,kBAAC,OAAD;EAAK,WAAU;YAAf;GAAgE;GAE9D,kBAAC,GAAD;IACE,SAAQ;IACR,OAAM;IACN,IAAI;KACF,IAAI;KACJ,UAAU;KACV,eAAe;KACf,eAAe;IACjB;IACA,WAAW,GAAG;cACf;GAEO,CAAA;GAAC;EAEN;;CAKX,SAAS;EACP,OACE,kBAAC,OAAD;GAAK,WAAW,SAAS,KAAK,MAAM;aAClC,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,KAAK,mBAAmB,KAAK,MAAM,YAAY;KAC/C,KAAK,aAAa,KAAK,OAAO,KAAK,KAAK;KACxC,KAAK,0BAA0B,KAAK,MAAM,KAAK;KAE/C,KAAK,qBAAqB,KAAK,KAAK,KACnC,kBAAC,OAAD,EAAA,UACE,kBAAC,GAAD;MACE,QAAQ,KAAK,MAAM;MACnB,UAAU,KAAK,MAAM;MACrB,UAAU,KAAK,MAAM;MACrB,WAAW,KAAK,MAAM;MACtB,cAAc,KAAK,MAAM;MACzB,WAAW,KAAK,MAAM;MACtB,WAAW,KAAK,MAAM;MACtB,gBAAgB,KAAK,MAAM;MAC3B,SAAS,MAAc;OACrB,KAAK,WAAW,CAAI;MACtB;MACA,WAAW,MAAc;OACvB,KAAK,WAAW,CAAI;MACtB;MACA,aAAa,KAAK,MAAM;KACzB,CAAA,EACE,CAAA;IAEJ;;EACF,CAAA;CAET;AACF;AAEA,SAAgB,EAAmB,GAAgC;CACjE,IAAM,IAAiB,EAAkB;CAEzC,OAAO,kBAAC,GAAD;EAAqB,GAAI;EAAO,OAAO,EAAe;CAAc,CAAA;AAC7E"}
1
+ {"version":3,"file":"SynapseFormWrapper.js","names":[],"sources":["../../../src/components/SynapseForm/SynapseFormWrapper.tsx"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { useSynapseContext } from '@/utils'\nimport { SRC_SIGN_IN_CLASS } from '@/utils/SynapseConstants'\nimport $RefParser from '@apidevtools/json-schema-ref-parser'\nimport { Button } from '@mui/material'\nimport { UiSchema } from '@rjsf/utils'\nimport { FormData } from '@sage-bionetworks/synapse-types'\nimport { get, includes } from 'lodash-es'\nimport { Component } from 'react'\nimport FullWidthAlert from '../FullWidthAlert'\nimport SynapseForm from './SynapseForm'\nimport { getFileEntityData } from './SynapseFormUtils'\nimport { StatusEnum } from './types'\n\n/**\n * TODO: SWC-5612 - Replace token prop with SynapseContext.accessToken\n *\n * This wasn't done because Enzyme's shallow renderer is not currently\n * compatible with the `contextType` field in the React 16+ context API.\n *\n * This can be fixed by rewriting tests to not rely on the shallow renderer.\n *\n * See here: https://github.com/enzymejs/enzyme/issues/1553\n */\n\nexport type SynapseFormWrapperProps = {\n // Provide the parent container (folder/project), that should contain a folder (named <user_id>) that this user can write to.\n formSchemaEntityId: string // Synapse file that contains the form schema.\n formUiSchemaEntityId: string // Synapse file that contains the form ui schema.\n formNavSchemaEntityId: string //Synapse file that consists screen nav schema\n token?: string // user's access token\n isWizardMode?: boolean // if we are displaying the form in wizard mode\n fileNamePath: string // path in data to specify the name of saved file\n formTitle: string //for UI customization\n formClass?: string // to support potential theming\n\n formDataId?: string //formDataId for user data form data\n dataFileHandleId?: string //fileHandle to get userData\n submitted?: boolean // if the file has been submitted\n formGroupId: string\n}\n\ntype SynapseFormWrapperState = {\n notification?: Notification\n isLoading?: boolean\n formDataId?: string // file holding user form data\n formData?: any // form data that prepopulates the form\n formSchema?: any // schema that drives the form\n formUiSchema?: UiSchema // ui schema that directs how to render the form elements\n formNavSchema?: any // drives the steps left panel\n\n status?: StatusEnum\n}\n\ntype Error = {\n name?: string\n message?: string\n}\n\ninterface Notification extends Error {\n type: StatusEnum\n}\n\nclass _SynapseFormWrapper extends Component<\n SynapseFormWrapperProps,\n SynapseFormWrapperState\n> {\n constructor(props: SynapseFormWrapperProps) {\n super(props)\n this.state = {\n isLoading: true,\n formDataId: this.props?.formDataId,\n }\n }\n\n componentDidMount() {\n this.getData(this.props.token)\n }\n\n componentDidUpdate(prevProps: SynapseFormWrapperProps) {\n const shouldUpdate = this.props.token !== prevProps.token\n if (shouldUpdate) {\n this.getData(this.props.token)\n }\n }\n\n //same as above but also uses $RefParser to convert json $refs to regular json\n getFileEntityDataDereferenced = async (\n token: string,\n entityId: string,\n versionNumber?: string,\n ): Promise<{ version?: number; content: JSON }> => {\n const { version, content } = await getFileEntityData(\n token,\n entityId,\n versionNumber,\n this.onError,\n )\n const derefContent = await $RefParser.dereference<JSON>(content)\n return {\n version: version,\n content: derefContent,\n }\n }\n\n getData = async (token?: string): Promise<void> => {\n if (!token) {\n return\n }\n try {\n let formData: Record<string, any> = {}\n const dataFileHandleId = this.props.dataFileHandleId\n const submitted = this.props.submitted\n let formSchemaVersion = undefined\n let uiSchemaVersion = undefined\n let navSchemaVersion = undefined\n\n //for not new form we need to get the data\n //and if it is submitted form we need to pull appropriate schema versions\n //for new form (no dataFileHandleId) we need to populate schema versions\n\n if (dataFileHandleId) {\n const fileData = await SynapseClient.getFileHandleContentFromID(\n dataFileHandleId,\n token,\n )\n formData = JSON.parse(fileData)\n if (submitted && formData?.['metadata']) {\n ;({ formSchemaVersion, uiSchemaVersion, navSchemaVersion } =\n formData['metadata'])\n }\n }\n\n const promises = [\n this.getFileEntityDataDereferenced(\n token,\n this.props.formSchemaEntityId,\n formSchemaVersion,\n ),\n getFileEntityData(\n token,\n this.props.formUiSchemaEntityId,\n uiSchemaVersion,\n this.onError,\n ),\n getFileEntityData(\n token,\n this.props.formNavSchemaEntityId,\n navSchemaVersion,\n this.onError,\n ),\n ]\n const configData = await Promise.all(promises)\n\n if (!dataFileHandleId) {\n //if we are creating a new file - store the versions\n formData = {\n metadata: {\n formSchemaVersion: configData[0].version,\n uiSchemaVersion: configData[1].version,\n navSchemaVersion: configData[2].version,\n },\n }\n }\n this.setState({\n formData: formData,\n formSchema: configData[0].content,\n formUiSchema: configData[1].content as unknown as UiSchema,\n formNavSchema: configData[2].content,\n isLoading: false,\n })\n } catch (error) {\n this.onError({ message: error })\n } finally {\n this.setState({\n isLoading: false,\n })\n }\n }\n\n finishedProcessing = (status: StatusEnum, message?: string) => {\n this.setState({\n isLoading: false,\n notification: { type: status, message: message },\n status: status,\n })\n //this will show the update message for 7 seconds\n setTimeout(() => {\n this.setState({ status: undefined })\n }, 7000)\n }\n\n onError = (error: Error) => {\n this.setState({\n notification: {\n type: StatusEnum.ERROR,\n message: error.message,\n name: error.name,\n },\n status: StatusEnum.ERROR,\n isLoading: false,\n })\n }\n\n submitForm = async (formData: any) => {\n await this.saveToFile(formData)\n\n this.setState({\n isLoading: true,\n })\n\n await SynapseClient.submitFormData(this.state.formDataId!, this.props.token)\n this.finishedProcessing(StatusEnum.SUBMIT_SUCCESS, 'File Submitted')\n }\n\n createOrUpdateFormDataFile = async (\n fileName: string,\n fileContentsBlob: Blob,\n ): Promise<FormData> => {\n fileName = `${fileName}.json`\n const fileUploadComplete = await SynapseClient.uploadFile(\n this.props.token,\n fileName,\n fileContentsBlob,\n )\n const formGroupId = this.props.formGroupId\n if (!formGroupId) {\n console.error('formGroupId must be defined')\n throw new Error('formGroupId must be defined')\n }\n try {\n // do we need to create a new file entity, or update an existing file entity?\n const newFileHandleId = fileUploadComplete.fileHandleId\n\n let formData\n if (this.state.formDataId) {\n formData = await SynapseClient.updateFormData(\n this.state.formDataId,\n fileName,\n newFileHandleId,\n this.props.token!,\n )\n } else {\n formData = await SynapseClient.createFormData(\n formGroupId,\n fileName,\n newFileHandleId,\n this.props.token!,\n )\n }\n\n return formData\n } catch (error) {\n this.onError(error)\n throw error\n }\n }\n\n saveToFile = async (data: any) => {\n const fileName = get(data, this.props.fileNamePath)\n this.setState({\n status: StatusEnum.PROGRESS,\n notification: { type: StatusEnum.PROGRESS, message: 'Progress' },\n isLoading: true,\n })\n\n if (!fileName) {\n // try to find corresponding property. By convention it should be first level property\n // and follow pattern screen.prop\n let errorTitle = 'File Name'\n try {\n // get it to the schema format\n const searchString = `${this.props.fileNamePath.replace(\n '.',\n '.properties.',\n )}.title`\n errorTitle = get(\n this.state.formSchema.properties,\n searchString,\n errorTitle,\n )\n } finally {\n const error = new Error(\n `Please Provide the ${errorTitle} before saving`,\n )\n this.onError(error)\n }\n return\n }\n\n try {\n const fileContent: Blob = new Blob([JSON.stringify(data)], {\n type: 'text/json',\n })\n\n const formData = await this.createOrUpdateFormDataFile(\n fileName,\n fileContent,\n )\n this.setState({\n formDataId: formData.formDataId,\n })\n\n this.finishedProcessing(StatusEnum.SAVE_SUCCESS, 'File Saved')\n } catch (error) {\n this.onError(error)\n }\n }\n\n isReadyToDisplayForm = (state: SynapseFormWrapperState): boolean => {\n return (\n this.state.status !== StatusEnum.ERROR_CRITICAL &&\n state.formSchema &&\n state.formUiSchema &&\n state.formNavSchema &&\n state.formData\n )\n }\n\n renderLoader = (\n state: SynapseFormWrapperState,\n props: SynapseFormWrapperProps,\n ) => {\n if (\n includes([StatusEnum.ERROR, StatusEnum.ERROR_CRITICAL], state.status) &&\n props.token &&\n state.isLoading\n ) {\n return (\n <div className=\"text-center\">\n <span className={'spinner'} />\n </div>\n )\n } else {\n return <></>\n }\n }\n\n renderNotification = (notification?: Notification) => {\n if (!notification) {\n return <></>\n }\n if (notification.type === StatusEnum.ERROR) {\n return (\n <FullWidthAlert\n variant=\"danger\"\n title=\"Error\"\n isGlobal={false}\n description={\n <>\n {notification.name} {notification.message}\n </>\n }\n onClose={() => this.setState({ status: undefined })}\n />\n )\n }\n\n return <></>\n }\n\n renderUnauthenticatedView = (token: string | undefined) => {\n if (token) {\n return <></>\n } else {\n return (\n <div className=\"panel padding-full unauthenticated text-center\">\n Please\n <Button\n variant=\"text\"\n color=\"primary\"\n sx={{\n mx: 1,\n fontSize: '1.4rem',\n verticalAlign: 'baseline',\n textTransform: 'none',\n }}\n className={`${SRC_SIGN_IN_CLASS}`}\n >\n sign in\n </Button>\n to initiate or continue your submission\n </div>\n )\n }\n }\n\n render() {\n return (\n <div className={`theme-${this.props.formClass}`}>\n <div className=\"SRC-ReactJsonForm\">\n {this.renderNotification(this.state.notification)}\n {this.renderLoader(this.state, this.props)}\n {this.renderUnauthenticatedView(this.props.token)}\n\n {this.isReadyToDisplayForm(this.state) && (\n <div>\n <SynapseForm\n schema={this.state.formSchema}\n uiSchema={this.state.formUiSchema!}\n formData={this.state.formData}\n navSchema={this.state.formNavSchema}\n isWizardMode={this.props.isWizardMode}\n formTitle={this.props.formTitle}\n formClass={this.props.formClass}\n callbackStatus={this.state.status}\n onSave={(data: any) => {\n this.saveToFile(data)\n }}\n onSubmit={(data: any) => {\n this.submitForm(data)\n }}\n isSubmitted={this.props.submitted}\n />\n </div>\n )}\n </div>\n </div>\n )\n }\n}\n\nexport function SynapseFormWrapper(props: SynapseFormWrapperProps) {\n const synapseContext = useSynapseContext()\n\n return <_SynapseFormWrapper {...props} token={synapseContext.accessToken} />\n}\n\nexport default SynapseFormWrapper\n"],"mappings":"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"}
@@ -1 +1 @@
1
- ._container_1beyq_1{background-color:#fff3;border-radius:9px;grid-template-columns:100%;justify-items:start;max-width:1300px;margin:auto;padding-top:0;padding-bottom:40px;display:grid}@media (width>=600px){._container_1beyq_1{grid-template-columns:50% 50%;padding-top:80px;padding-bottom:60px}}._item_1beyq_20{border-right:1px solid var(--synapse-gray-400);border-bottom:1px solid var(--synapse-gray-400);border-left:1px solid var(--synapse-gray-400);grid-template-columns:80% 20%;display:grid}._item_1beyq_20:first-child{border-top:1px solid var(--synapse-gray-400)}@media (width>=600px){._item_1beyq_20{border-top:none;border-left:none}._item_1beyq_20:nth-child(odd){border-left:1px solid var(--synapse-gray-400)}._item_1beyq_20:nth-child(-n+2){border-top:1px solid var(--synapse-gray-400)}}._item_1beyq_20{background-color:#ffffffe6;width:100%;padding:15px 0;text-decoration:none}._item_1beyq_20:hover{cursor:pointer;background-color:#e9f3fef2;text-decoration:none!important}._item_1beyq_20 ._itemTitle_1beyq_53{color:var(--synapse-gray-900);font-size:16px;font-weight:700}._item_1beyq_20 ._itemMeta_1beyq_58{color:var(--synapse-gray-700)}._adkpVariant_1beyq_62{background-color:#0000!important}._adkpVariant_1beyq_62 ._item_1beyq_20:hover{background:linear-gradient(91deg,#7761b7 5.34%,#9067b6 24.31%,#816dc8 91.15%)}._adkpVariant_1beyq_62 ._item_1beyq_20:hover ._itemTitle_1beyq_53,._adkpVariant_1beyq_62 ._item_1beyq_20:hover ._itemMeta_1beyq_58,._adkpVariant_1beyq_62 ._item_1beyq_20:hover ._itemDescription_1beyq_70{color:#fff}._itemContent_1beyq_74{color:var(--synapse-gray-900);padding-left:15px;padding-right:10px}._itemAction_1beyq_80{place-self:start end;padding-right:15px}._chip_1beyq_86.MuiChip-root{border-radius:5px;height:24px}
1
+ ._container_1ocs6_1{background-color:#fff3;border-radius:9px;grid-template-columns:100%;justify-items:start;max-width:1300px;margin:auto;padding-top:0;padding-bottom:40px;display:grid}@media (width>=600px){._container_1ocs6_1{grid-template-columns:50% 50%;padding-top:80px;padding-bottom:60px}}._item_1ocs6_20{border-right:1px solid var(--synapse-gray-400);border-bottom:1px solid var(--synapse-gray-400);border-left:1px solid var(--synapse-gray-400);grid-template-columns:80% 20%;display:grid}._item_1ocs6_20:first-child{border-top:1px solid var(--synapse-gray-400)}@media (width>=600px){._item_1ocs6_20{border-top:none;border-left:none}._item_1ocs6_20:nth-child(odd){border-left:1px solid var(--synapse-gray-400)}._item_1ocs6_20:nth-child(-n+2){border-top:1px solid var(--synapse-gray-400)}}._item_1ocs6_20{background-color:#ffffffe6;width:100%;padding:15px 0;text-decoration:none}._item_1ocs6_20:hover{cursor:pointer;background-color:#e9f3fef2;text-decoration:none!important}._item_1ocs6_20 ._itemTitle_1ocs6_53{color:var(--synapse-gray-900);font-size:16px;font-weight:700}._item_1ocs6_20 ._itemMeta_1ocs6_58{color:var(--synapse-gray-700)}._adkpVariant_1ocs6_62{grid-template-columns:100%;background-color:#0000!important}@media (width>=600px){._adkpVariant_1ocs6_62{padding-top:0}}._adkpVariant_1ocs6_62 ._item_1ocs6_20{border-left:1px solid var(--synapse-gray-400);border-top:none}._adkpVariant_1ocs6_62 ._item_1ocs6_20:first-child{border-top:1px solid var(--synapse-gray-400)}._adkpVariant_1ocs6_62 ._item_1ocs6_20:hover{background:linear-gradient(91deg,#7761b7 5.34%,#9067b6 24.31%,#816dc8 91.15%)}._adkpVariant_1ocs6_62 ._item_1ocs6_20:hover ._itemTitle_1ocs6_53,._adkpVariant_1ocs6_62 ._item_1ocs6_20:hover ._itemMeta_1ocs6_58,._adkpVariant_1ocs6_62 ._item_1ocs6_20:hover ._itemDescription_1ocs6_83{color:#fff}._itemContent_1ocs6_87{color:var(--synapse-gray-900);padding-left:15px;padding-right:10px}._itemAction_1ocs6_93{place-self:start end;padding-right:15px}._chip_1ocs6_99.MuiChip-root{border-radius:5px;height:24px}
@@ -1,13 +1,13 @@
1
1
  import './HotdropsAndNews.css';var e = {
2
- container: "_container_1beyq_1",
3
- item: "_item_1beyq_20",
4
- itemTitle: "_itemTitle_1beyq_53",
5
- itemMeta: "_itemMeta_1beyq_58",
6
- adkpVariant: "_adkpVariant_1beyq_62",
7
- itemDescription: "_itemDescription_1beyq_70",
8
- itemContent: "_itemContent_1beyq_74",
9
- itemAction: "_itemAction_1beyq_80",
10
- chip: "_chip_1beyq_86"
2
+ container: "_container_1ocs6_1",
3
+ item: "_item_1ocs6_20",
4
+ itemTitle: "_itemTitle_1ocs6_53",
5
+ itemMeta: "_itemMeta_1ocs6_58",
6
+ adkpVariant: "_adkpVariant_1ocs6_62",
7
+ itemDescription: "_itemDescription_1ocs6_83",
8
+ itemContent: "_itemContent_1ocs6_87",
9
+ itemAction: "_itemAction_1ocs6_93",
10
+ chip: "_chip_1ocs6_99"
11
11
  };
12
12
  //#endregion
13
13
  export { e as default };
@@ -1 +1 @@
1
- {"version":3,"file":"HotdropsAndNews.module.js","names":[],"sources":["../../../src/components/SynapseHomepageV2/HotdropsAndNews.module.scss"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Grid container (SynapseHotDrops / WordPressNews outer wrapper)\n// ---------------------------------------------------------------------------\n\n.container {\n display: grid;\n justify-items: start;\n background-color: rgba(255, 255, 255, 0.2);\n border-radius: 9px;\n max-width: 1300px;\n margin: auto;\n\n // Mobile-first: single column\n grid-template-columns: 100%;\n padding-top: 0;\n padding-bottom: 40px;\n\n // ≥ MUI 'sm' breakpoint (600px)\n @media (min-width: 600px) {\n grid-template-columns: 50% 50%;\n padding-top: 80px;\n padding-bottom: 60px;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Individual item row\n// ---------------------------------------------------------------------------\n\n.item {\n display: grid;\n grid-template-columns: 80% 20%;\n // Each item provides its own right + bottom edge.\n // Leading edges (top/left) are added selectively to avoid doubling.\n border-right: 1px solid var(--synapse-gray-400); // lighter interior border\n border-bottom: 1px solid var(--synapse-gray-400);\n\n // Mobile: single column — every item is the left edge; only the first has a top edge.\n border-left: 1px solid var(--synapse-gray-400);\n &:first-child {\n border-top: 1px solid var(--synapse-gray-400);\n }\n\n // Desktop: two columns — left edge belongs to odd items (col 1); top edge to the first two (row 1).\n @media (min-width: 600px) {\n border-left: none;\n border-top: none;\n\n &:nth-child(odd) {\n border-left: 1px solid var(--synapse-gray-400);\n }\n\n &:nth-child(-n + 2) {\n border-top: 1px solid var(--synapse-gray-400);\n }\n }\n\n width: 100%;\n background-color: rgba(255, 255, 255, 0.9);\n padding: 15px 0;\n\n text-decoration: none;\n\n &:hover {\n background-color: rgba(233, 243, 254, 0.95);\n cursor: pointer;\n text-decoration: none !important;\n }\n\n .itemTitle {\n color: var(--synapse-gray-900);\n font-size: 16px;\n font-weight: 700;\n }\n .itemMeta {\n color: var(--synapse-gray-700);\n }\n}\n\n.adkpVariant {\n background-color: transparent !important;\n\n .item:hover {\n background: linear-gradient(\n 91deg,\n #7761b7 5.34%,\n #9067b6 24.31%,\n #816dc8 91.15%\n );\n\n .itemTitle,\n .itemMeta,\n .itemDescription {\n color: #fff;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Item: left content area\n// ---------------------------------------------------------------------------\n\n.itemContent {\n color: var(--synapse-gray-900);\n padding-left: 15px;\n padding-right: 10px;\n}\n\n// ---------------------------------------------------------------------------\n// Item: right action area (chip container)\n// ---------------------------------------------------------------------------\n\n.itemAction {\n justify-self: end;\n align-self: start;\n padding-right: 15px;\n}\n\n// ---------------------------------------------------------------------------\n// Category / type chip\n// ---------------------------------------------------------------------------\n\n.chip:global(.MuiChip-root) {\n border-radius: 5px;\n height: 24px;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"HotdropsAndNews.module.js","names":[],"sources":["../../../src/components/SynapseHomepageV2/HotdropsAndNews.module.scss"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Grid container (SynapseHotDrops / WordPressNews outer wrapper)\n// ---------------------------------------------------------------------------\n\n.container {\n display: grid;\n justify-items: start;\n background-color: rgba(255, 255, 255, 0.2);\n border-radius: 9px;\n max-width: 1300px;\n margin: auto;\n\n // Mobile-first: single column\n grid-template-columns: 100%;\n padding-top: 0;\n padding-bottom: 40px;\n\n // ≥ MUI 'sm' breakpoint (600px)\n @media (min-width: 600px) {\n grid-template-columns: 50% 50%;\n padding-top: 80px;\n padding-bottom: 60px;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Individual item row\n// ---------------------------------------------------------------------------\n\n.item {\n display: grid;\n grid-template-columns: 80% 20%;\n // Each item provides its own right + bottom edge.\n // Leading edges (top/left) are added selectively to avoid doubling.\n border-right: 1px solid var(--synapse-gray-400); // lighter interior border\n border-bottom: 1px solid var(--synapse-gray-400);\n\n // Mobile: single column — every item is the left edge; only the first has a top edge.\n border-left: 1px solid var(--synapse-gray-400);\n &:first-child {\n border-top: 1px solid var(--synapse-gray-400);\n }\n\n // Desktop: two columns — left edge belongs to odd items (col 1); top edge to the first two (row 1).\n @media (min-width: 600px) {\n border-left: none;\n border-top: none;\n\n &:nth-child(odd) {\n border-left: 1px solid var(--synapse-gray-400);\n }\n\n &:nth-child(-n + 2) {\n border-top: 1px solid var(--synapse-gray-400);\n }\n }\n\n width: 100%;\n background-color: rgba(255, 255, 255, 0.9);\n padding: 15px 0;\n\n text-decoration: none;\n\n &:hover {\n background-color: rgba(233, 243, 254, 0.95);\n cursor: pointer;\n text-decoration: none !important;\n }\n\n .itemTitle {\n color: var(--synapse-gray-900);\n font-size: 16px;\n font-weight: 700;\n }\n .itemMeta {\n color: var(--synapse-gray-700);\n }\n}\n\n.adkpVariant {\n background-color: transparent !important;\n grid-template-columns: 100%;\n\n @media (min-width: 600px) {\n padding-top: 0;\n }\n\n .item {\n border-left: 1px solid var(--synapse-gray-400);\n border-top: none;\n\n &:first-child {\n border-top: 1px solid var(--synapse-gray-400);\n }\n }\n\n .item:hover {\n background: linear-gradient(\n 91deg,\n #7761b7 5.34%,\n #9067b6 24.31%,\n #816dc8 91.15%\n );\n\n .itemTitle,\n .itemMeta,\n .itemDescription {\n color: #fff;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Item: left content area\n// ---------------------------------------------------------------------------\n\n.itemContent {\n color: var(--synapse-gray-900);\n padding-left: 15px;\n padding-right: 10px;\n}\n\n// ---------------------------------------------------------------------------\n// Item: right action area (chip container)\n// ---------------------------------------------------------------------------\n\n.itemAction {\n justify-self: end;\n align-self: start;\n padding-right: 15px;\n}\n\n// ---------------------------------------------------------------------------\n// Category / type chip\n// ---------------------------------------------------------------------------\n\n.chip:global(.MuiChip-root) {\n border-radius: 5px;\n height: 24px;\n}\n"],"mappings":""}
@@ -79,6 +79,20 @@
79
79
 
80
80
  .adkpVariant {
81
81
  background-color: transparent !important;
82
+ grid-template-columns: 100%;
83
+
84
+ @media (min-width: 600px) {
85
+ padding-top: 0;
86
+ }
87
+
88
+ .item {
89
+ border-left: 1px solid var(--synapse-gray-400);
90
+ border-top: none;
91
+
92
+ &:first-child {
93
+ border-top: 1px solid var(--synapse-gray-400);
94
+ }
95
+ }
82
96
 
83
97
  .item:hover {
84
98
  background: linear-gradient(
@@ -8,7 +8,7 @@ function f({ gotoPlace: f }) {
8
8
  let p = l(), [m, h] = n(""), [g, _] = n(""), [v, y] = n(0), b = () => {
9
9
  if (v == 0) {
10
10
  let n = m.toLowerCase().trim(), r = e(n);
11
- if (r && r.targetVersionNumber) {
11
+ if (r?.targetVersionNumber) {
12
12
  f(`/Synapse:${`${r.targetId}.${r.targetVersionNumber}`}`);
13
13
  return;
14
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SynapseHomepageChatSearch.js","names":[],"sources":["../../../src/components/SynapseHomepageV2/SynapseHomepageChatSearch.tsx"],"sourcesContent":["import {\n Box,\n FormControl,\n InputAdornment,\n MenuItem,\n OutlinedInput,\n Select,\n useTheme,\n} from '@mui/material'\nimport { Color } from '@mui/material/styles'\nimport { FormEventHandler, KeyboardEventHandler, useState } from 'react'\nimport { getSearchToken } from '../SynapseNavDrawer/SynapseNavDrawer'\nimport { parseSynId } from '@/utils/functions/RegularExpressions'\n\nexport type SynapseHomepageChatSearchProps = {\n gotoPlace: (href: string) => void\n}\nenum SearchMode {\n SEARCH = 0,\n CHAT = 1,\n}\n\n/**\n * Synapse Homepage Search component with a dropdown \"autocomplete\" menu of options.\n * SWC-7005: Not used in the current implementation of the Home Page, but there are plans to utilize it later.\n * @returns\n */\nexport function SynapseHomepageChatSearch({\n gotoPlace,\n}: SynapseHomepageChatSearchProps) {\n const theme = useTheme()\n const [searchValue, setSearchValue] = useState('')\n const [chatValue, setChatValue] = useState('')\n const [mode, setMode] = useState(SearchMode.SEARCH)\n\n const executeSearch = () => {\n if (mode == SearchMode.SEARCH) {\n const searchValueCleaned = searchValue.toLowerCase().trim()\n\n // Check if the input is a valid Synapse ID with version\n const parsedSynId = parseSynId(searchValueCleaned)\n if (parsedSynId && parsedSynId.targetVersionNumber) {\n const synIdWithVersion = `${parsedSynId.targetId}.${parsedSynId.targetVersionNumber}`\n gotoPlace(`/Synapse:${synIdWithVersion}`)\n return\n }\n gotoPlace(\n `/SearchV2:default?query=${getSearchToken(\n searchValueCleaned.split(/[ ,]+/),\n )}`,\n )\n } else {\n gotoPlace(`/Chat:initialMessage=${encodeURIComponent(chatValue)}`)\n }\n }\n const handleSubmit: FormEventHandler<HTMLFormElement> = event => {\n event.preventDefault()\n executeSearch()\n }\n const handleKeyDown: KeyboardEventHandler<\n HTMLTextAreaElement | HTMLInputElement\n > = event => {\n if (event.key === 'Enter') {\n event.preventDefault()\n executeSearch()\n }\n }\n\n return (\n <Box component=\"form\" onSubmit={handleSubmit}>\n <FormControl fullWidth sx={{ m: 1 }}>\n <OutlinedInput\n value={mode == SearchMode.SEARCH ? searchValue : chatValue}\n startAdornment={\n <InputAdornment\n position=\"start\"\n sx={{ p: '0px', ml: '7px', mr: '13px' }}\n >\n <Select\n value={mode}\n sx={{\n fontSize: '24px',\n fontWeight: 400,\n minWidth: '125px',\n color: (theme.palette.secondary as unknown as Color)[600],\n '& div[role=\"combobox\"]': {\n p: '14px 12px',\n },\n }}\n onChange={v => {\n setMode(v.target.value as SearchMode)\n //restore search or chat value\n }}\n >\n <MenuItem value={SearchMode.SEARCH}>Search</MenuItem>\n <MenuItem value={SearchMode.CHAT}>Chat</MenuItem>\n </Select>\n </InputAdornment>\n }\n placeholder={\n mode == SearchMode.SEARCH ? 'Search Synapse' : 'Ask a question'\n }\n sx={{ fontSize: '24px', borderRadius: 96.6 }}\n onChange={event => {\n if (mode == SearchMode.SEARCH) {\n setSearchValue(event.target.value)\n } else {\n setChatValue(event.target.value)\n }\n }}\n onKeyDown={handleKeyDown}\n />\n </FormControl>\n </Box>\n )\n}\n"],"mappings":";;;;;;AA2BA,SAAgB,EAA0B,EACxC,gBACiC;CACjC,IAAM,IAAQ,EAAS,GACjB,CAAC,GAAa,KAAkB,EAAS,EAAE,GAC3C,CAAC,GAAW,KAAgB,EAAS,EAAE,GACvC,CAAC,GAAM,KAAW,EAAA,CAA0B,GAE5C,UAAsB;EAC1B,IAAI,KAAA,GAA2B;GAC7B,IAAM,IAAqB,EAAY,YAAY,EAAE,KAAK,GAGpD,IAAc,EAAW,CAAkB;GACjD,IAAI,KAAe,EAAY,qBAAqB;IAElD,EAAU,YAAY,GADM,EAAY,SAAS,GAAG,EAAY,uBACxB;IACxC;GACF;GACA,EACE,2BAA2B,EACzB,EAAmB,MAAM,OAAO,CAClC,GACF;EACF,OACE,EAAU,wBAAwB,mBAAmB,CAAS,GAAG;CAErE;CAcA,OACE,kBAAC,GAAD;EAAK,WAAU;EAAO,WAdgC,MAAS;GAE/D,AADA,EAAM,eAAe,GACrB,EAAc;EAChB;YAYI,kBAAC,GAAD;GAAa,WAAA;GAAU,IAAI,EAAE,GAAG,EAAE;aAChC,kBAAC,GAAD;IACE,OAAO,KAAA,IAA4B,IAAc;IACjD,gBACE,kBAAC,GAAD;KACE,UAAS;KACT,IAAI;MAAE,GAAG;MAAO,IAAI;MAAO,IAAI;KAAO;eAEtC,kBAAC,GAAD;MACE,OAAO;MACP,IAAI;OACF,UAAU;OACV,YAAY;OACZ,UAAU;OACV,OAAQ,EAAM,QAAQ,UAA+B;OACrD,4BAA0B,EACxB,GAAG,YACL;MACF;MACA,WAAU,MAAK;OACb,EAAQ,EAAE,OAAO,KAAmB;MAEtC;gBAdF,CAgBE,kBAAC,GAAD;OAAU,OAAA;iBAA0B;MAAgB,CAAA,GACpD,kBAAC,GAAD;OAAU,OAAA;iBAAwB;MAAc,CAAA,CAC1C;;IACM,CAAA;IAElB,aACE,KAAA,IAA4B,mBAAmB;IAEjD,IAAI;KAAE,UAAU;KAAQ,cAAc;IAAK;IAC3C,WAAU,MAAS;KACjB,AAAI,KAAA,IACF,EAAe,EAAM,OAAO,KAAK,IAEjC,EAAa,EAAM,OAAO,KAAK;IAEnC;IACA,YAjDJ,MAAS;KACX,AAAI,EAAM,QAAQ,YAChB,EAAM,eAAe,GACrB,EAAc;IAElB;GA6CO,CAAA;EACU,CAAA;CACV,CAAA;AAET"}
1
+ {"version":3,"file":"SynapseHomepageChatSearch.js","names":[],"sources":["../../../src/components/SynapseHomepageV2/SynapseHomepageChatSearch.tsx"],"sourcesContent":["import {\n Box,\n FormControl,\n InputAdornment,\n MenuItem,\n OutlinedInput,\n Select,\n useTheme,\n} from '@mui/material'\nimport { Color } from '@mui/material/styles'\nimport { FormEventHandler, KeyboardEventHandler, useState } from 'react'\nimport { getSearchToken } from '../SynapseNavDrawer/SynapseNavDrawer'\nimport { parseSynId } from '@/utils/functions/RegularExpressions'\n\nexport type SynapseHomepageChatSearchProps = {\n gotoPlace: (href: string) => void\n}\nenum SearchMode {\n SEARCH = 0,\n CHAT = 1,\n}\n\n/**\n * Synapse Homepage Search component with a dropdown \"autocomplete\" menu of options.\n * SWC-7005: Not used in the current implementation of the Home Page, but there are plans to utilize it later.\n * @returns\n */\nexport function SynapseHomepageChatSearch({\n gotoPlace,\n}: SynapseHomepageChatSearchProps) {\n const theme = useTheme()\n const [searchValue, setSearchValue] = useState('')\n const [chatValue, setChatValue] = useState('')\n const [mode, setMode] = useState(SearchMode.SEARCH)\n\n const executeSearch = () => {\n if (mode == SearchMode.SEARCH) {\n const searchValueCleaned = searchValue.toLowerCase().trim()\n\n // Check if the input is a valid Synapse ID with version\n const parsedSynId = parseSynId(searchValueCleaned)\n if (parsedSynId?.targetVersionNumber) {\n const synIdWithVersion = `${parsedSynId.targetId}.${parsedSynId.targetVersionNumber}`\n gotoPlace(`/Synapse:${synIdWithVersion}`)\n return\n }\n gotoPlace(\n `/SearchV2:default?query=${getSearchToken(\n searchValueCleaned.split(/[ ,]+/),\n )}`,\n )\n } else {\n gotoPlace(`/Chat:initialMessage=${encodeURIComponent(chatValue)}`)\n }\n }\n const handleSubmit: FormEventHandler<HTMLFormElement> = event => {\n event.preventDefault()\n executeSearch()\n }\n const handleKeyDown: KeyboardEventHandler<\n HTMLTextAreaElement | HTMLInputElement\n > = event => {\n if (event.key === 'Enter') {\n event.preventDefault()\n executeSearch()\n }\n }\n\n return (\n <Box component=\"form\" onSubmit={handleSubmit}>\n <FormControl fullWidth sx={{ m: 1 }}>\n <OutlinedInput\n value={mode == SearchMode.SEARCH ? searchValue : chatValue}\n startAdornment={\n <InputAdornment\n position=\"start\"\n sx={{ p: '0px', ml: '7px', mr: '13px' }}\n >\n <Select\n value={mode}\n sx={{\n fontSize: '24px',\n fontWeight: 400,\n minWidth: '125px',\n color: (theme.palette.secondary as unknown as Color)[600],\n '& div[role=\"combobox\"]': {\n p: '14px 12px',\n },\n }}\n onChange={v => {\n setMode(v.target.value as SearchMode)\n //restore search or chat value\n }}\n >\n <MenuItem value={SearchMode.SEARCH}>Search</MenuItem>\n <MenuItem value={SearchMode.CHAT}>Chat</MenuItem>\n </Select>\n </InputAdornment>\n }\n placeholder={\n mode == SearchMode.SEARCH ? 'Search Synapse' : 'Ask a question'\n }\n sx={{ fontSize: '24px', borderRadius: 96.6 }}\n onChange={event => {\n if (mode == SearchMode.SEARCH) {\n setSearchValue(event.target.value)\n } else {\n setChatValue(event.target.value)\n }\n }}\n onKeyDown={handleKeyDown}\n />\n </FormControl>\n </Box>\n )\n}\n"],"mappings":";;;;;;AA2BA,SAAgB,EAA0B,EACxC,gBACiC;CACjC,IAAM,IAAQ,EAAS,GACjB,CAAC,GAAa,KAAkB,EAAS,EAAE,GAC3C,CAAC,GAAW,KAAgB,EAAS,EAAE,GACvC,CAAC,GAAM,KAAW,EAAA,CAA0B,GAE5C,UAAsB;EAC1B,IAAI,KAAA,GAA2B;GAC7B,IAAM,IAAqB,EAAY,YAAY,EAAE,KAAK,GAGpD,IAAc,EAAW,CAAkB;GACjD,IAAI,GAAa,qBAAqB;IAEpC,EAAU,YAAY,GADM,EAAY,SAAS,GAAG,EAAY,uBACxB;IACxC;GACF;GACA,EACE,2BAA2B,EACzB,EAAmB,MAAM,OAAO,CAClC,GACF;EACF,OACE,EAAU,wBAAwB,mBAAmB,CAAS,GAAG;CAErE;CAcA,OACE,kBAAC,GAAD;EAAK,WAAU;EAAO,WAdgC,MAAS;GAE/D,AADA,EAAM,eAAe,GACrB,EAAc;EAChB;YAYI,kBAAC,GAAD;GAAa,WAAA;GAAU,IAAI,EAAE,GAAG,EAAE;aAChC,kBAAC,GAAD;IACE,OAAO,KAAA,IAA4B,IAAc;IACjD,gBACE,kBAAC,GAAD;KACE,UAAS;KACT,IAAI;MAAE,GAAG;MAAO,IAAI;MAAO,IAAI;KAAO;eAEtC,kBAAC,GAAD;MACE,OAAO;MACP,IAAI;OACF,UAAU;OACV,YAAY;OACZ,UAAU;OACV,OAAQ,EAAM,QAAQ,UAA+B;OACrD,4BAA0B,EACxB,GAAG,YACL;MACF;MACA,WAAU,MAAK;OACb,EAAQ,EAAE,OAAO,KAAmB;MAEtC;gBAdF,CAgBE,kBAAC,GAAD;OAAU,OAAA;iBAA0B;MAAgB,CAAA,GACpD,kBAAC,GAAD;OAAU,OAAA;iBAAwB;MAAc,CAAA,CAC1C;;IACM,CAAA;IAElB,aACE,KAAA,IAA4B,mBAAmB;IAEjD,IAAI;KAAE,UAAU;KAAQ,cAAc;IAAK;IAC3C,WAAU,MAAS;KACjB,AAAI,KAAA,IACF,EAAe,EAAM,OAAO,KAAK,IAEjC,EAAa,EAAM,OAAO,KAAK;IAEnC;IACA,YAjDJ,MAAS;KACX,AAAI,EAAM,QAAQ,YAChB,EAAM,eAAe,GACrB,EAAc;IAElB;GA6CO,CAAA;EACU,CAAA;CACV,CAAA;AAET"}
@@ -28,7 +28,7 @@ function d() {
28
28
  }, [g]), g ? null : /* @__PURE__ */ l(a, {
29
29
  query: m,
30
30
  setQuery: (t) => {
31
- d((n) => (t && t.queryTerm && t.queryTerm.length > 0 ? n.set(e, JSON.stringify(t)) : n.delete(e), n));
31
+ d((n) => (t?.queryTerm && t.queryTerm.length > 0 ? n.set(e, JSON.stringify(t)) : n.delete(e), n));
32
32
  }
33
33
  });
34
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SynapseSearchPage.js","names":[],"sources":["../../../src/components/SynapseSearchPageResults/SynapseSearchPage.tsx"],"sourcesContent":["import { useSearchParams } from 'react-router'\nimport { SEARCH_PAGE_QUERY_PARAM } from '@/utils/SynapseConstants'\nimport { SynapseSearchPageResults } from './SynapseSearchPageResults'\nimport SynapseSearchPageRouter, {\n SynapseSearchPageRouterProps,\n} from './SynapseSearchPageRouter'\nimport { SearchQuery } from '@sage-bionetworks/synapse-types'\nimport { DEFAULT_SEARCH_QUERY } from '@/utils/searchDefaults'\nimport { useEffect, useMemo } from 'react'\nimport { parseSynId } from '@/utils/functions/RegularExpressions'\nimport { BackendDestinationEnum, getEndpoint } from '@/utils/functions'\n\nfunction SearchPageInternal() {\n const [searchParams, setSearchParams] = useSearchParams()\n\n // Parse SearchQuery object from URL parameter\n const queryParam = searchParams.get(SEARCH_PAGE_QUERY_PARAM) || ''\n let parsedURLQuery = {}\n try {\n parsedURLQuery = queryParam ? JSON.parse(queryParam) : {}\n } catch (error) {\n console.error('Error parsing search query from URL:', error)\n }\n\n const query: SearchQuery = {\n ...DEFAULT_SEARCH_QUERY,\n ...parsedURLQuery,\n }\n\n const queryTerm = query?.queryTerm?.[0] || ''\n const parsedSynId = useMemo(() => parseSynId(queryTerm), [queryTerm])\n\n useEffect(() => {\n if (parsedSynId) {\n const entityPath = parsedSynId.targetVersionNumber\n ? `${parsedSynId.targetId}.${parsedSynId.targetVersionNumber}`\n : parsedSynId.targetId\n\n window.location.assign(\n `${getEndpoint(\n BackendDestinationEnum.PORTAL_ENDPOINT,\n )}Synapse:${entityPath}`,\n )\n }\n }, [parsedSynId])\n\n // Store the whole SearchQuery object as JSON in URL\n const handleQueryChange = (newQuery: SearchQuery) => {\n setSearchParams(prev => {\n if (newQuery && newQuery.queryTerm && newQuery.queryTerm.length > 0) {\n prev.set(SEARCH_PAGE_QUERY_PARAM, JSON.stringify(newQuery))\n } else {\n prev.delete(SEARCH_PAGE_QUERY_PARAM)\n }\n return prev\n })\n }\n\n if (parsedSynId) {\n return null\n }\n\n // Render search results with new query\n return <SynapseSearchPageResults query={query} setQuery={handleQueryChange} />\n}\n\nexport type SynapseSearchPageProps = Omit<\n SynapseSearchPageRouterProps,\n 'children'\n>\n\nexport default function SynapseSearchPage(props: SynapseSearchPageProps) {\n return (\n <SynapseSearchPageRouter {...props}>\n <SearchPageInternal />\n </SynapseSearchPageRouter>\n )\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAS,IAAqB;CAC5B,IAAM,CAAC,GAAc,KAAmB,EAAgB,GAGlD,IAAa,EAAa,IAAA,OAA2B,KAAK,IAC5D,IAAiB,CAAC;CACtB,IAAI;EACF,IAAiB,IAAa,KAAK,MAAM,CAAU,IAAI,CAAC;CAC1D,SAAS,GAAO;EACd,QAAQ,MAAM,wCAAwC,CAAK;CAC7D;CAEA,IAAM,IAAqB;EACzB,GAAG;EACH,GAAG;CACL,GAEM,IAAY,GAAO,YAAY,MAAM,IACrC,IAAc,QAAc,EAAW,CAAS,GAAG,CAAC,CAAS,CAAC;CAiCpE,OA/BA,QAAgB;EACd,IAAI,GAAa;GACf,IAAM,IAAa,EAAY,sBAC3B,GAAG,EAAY,SAAS,GAAG,EAAY,wBACvC,EAAY;GAEhB,OAAO,SAAS,OACd,GAAG,EACD,EAAuB,eACzB,EAAE,UAAU,GACd;EACF;CACF,GAAG,CAAC,CAAW,CAAC,GAcZ,IACK,OAIF,kBAAC,GAAD;EAAiC;EAAO,WAhBpB,MAA0B;GACnD,GAAgB,OACV,KAAY,EAAS,aAAa,EAAS,UAAU,SAAS,IAChE,EAAK,IAAI,GAAyB,KAAK,UAAU,CAAQ,CAAC,IAE1D,EAAK,OAAO,CAAuB,GAE9B,EACR;EACH;CAO6E,CAAA;AAC/E;AAOA,SAAwB,EAAkB,GAA+B;CACvE,OACE,kBAAC,GAAD;EAAyB,GAAI;YAC3B,kBAAC,GAAD,CAAqB,CAAA;CACE,CAAA;AAE7B"}
1
+ {"version":3,"file":"SynapseSearchPage.js","names":[],"sources":["../../../src/components/SynapseSearchPageResults/SynapseSearchPage.tsx"],"sourcesContent":["import { useSearchParams } from 'react-router'\nimport { SEARCH_PAGE_QUERY_PARAM } from '@/utils/SynapseConstants'\nimport { SynapseSearchPageResults } from './SynapseSearchPageResults'\nimport SynapseSearchPageRouter, {\n SynapseSearchPageRouterProps,\n} from './SynapseSearchPageRouter'\nimport { SearchQuery } from '@sage-bionetworks/synapse-types'\nimport { DEFAULT_SEARCH_QUERY } from '@/utils/searchDefaults'\nimport { useEffect, useMemo } from 'react'\nimport { parseSynId } from '@/utils/functions/RegularExpressions'\nimport { BackendDestinationEnum, getEndpoint } from '@/utils/functions'\n\nfunction SearchPageInternal() {\n const [searchParams, setSearchParams] = useSearchParams()\n\n // Parse SearchQuery object from URL parameter\n const queryParam = searchParams.get(SEARCH_PAGE_QUERY_PARAM) || ''\n let parsedURLQuery = {}\n try {\n parsedURLQuery = queryParam ? JSON.parse(queryParam) : {}\n } catch (error) {\n console.error('Error parsing search query from URL:', error)\n }\n\n const query: SearchQuery = {\n ...DEFAULT_SEARCH_QUERY,\n ...parsedURLQuery,\n }\n\n const queryTerm = query?.queryTerm?.[0] || ''\n const parsedSynId = useMemo(() => parseSynId(queryTerm), [queryTerm])\n\n useEffect(() => {\n if (parsedSynId) {\n const entityPath = parsedSynId.targetVersionNumber\n ? `${parsedSynId.targetId}.${parsedSynId.targetVersionNumber}`\n : parsedSynId.targetId\n\n window.location.assign(\n `${getEndpoint(\n BackendDestinationEnum.PORTAL_ENDPOINT,\n )}Synapse:${entityPath}`,\n )\n }\n }, [parsedSynId])\n\n // Store the whole SearchQuery object as JSON in URL\n const handleQueryChange = (newQuery: SearchQuery) => {\n setSearchParams(prev => {\n if (newQuery?.queryTerm && newQuery.queryTerm.length > 0) {\n prev.set(SEARCH_PAGE_QUERY_PARAM, JSON.stringify(newQuery))\n } else {\n prev.delete(SEARCH_PAGE_QUERY_PARAM)\n }\n return prev\n })\n }\n\n if (parsedSynId) {\n return null\n }\n\n // Render search results with new query\n return <SynapseSearchPageResults query={query} setQuery={handleQueryChange} />\n}\n\nexport type SynapseSearchPageProps = Omit<\n SynapseSearchPageRouterProps,\n 'children'\n>\n\nexport default function SynapseSearchPage(props: SynapseSearchPageProps) {\n return (\n <SynapseSearchPageRouter {...props}>\n <SearchPageInternal />\n </SynapseSearchPageRouter>\n )\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAS,IAAqB;CAC5B,IAAM,CAAC,GAAc,KAAmB,EAAgB,GAGlD,IAAa,EAAa,IAAA,OAA2B,KAAK,IAC5D,IAAiB,CAAC;CACtB,IAAI;EACF,IAAiB,IAAa,KAAK,MAAM,CAAU,IAAI,CAAC;CAC1D,SAAS,GAAO;EACd,QAAQ,MAAM,wCAAwC,CAAK;CAC7D;CAEA,IAAM,IAAqB;EACzB,GAAG;EACH,GAAG;CACL,GAEM,IAAY,GAAO,YAAY,MAAM,IACrC,IAAc,QAAc,EAAW,CAAS,GAAG,CAAC,CAAS,CAAC;CAiCpE,OA/BA,QAAgB;EACd,IAAI,GAAa;GACf,IAAM,IAAa,EAAY,sBAC3B,GAAG,EAAY,SAAS,GAAG,EAAY,wBACvC,EAAY;GAEhB,OAAO,SAAS,OACd,GAAG,EACD,EAAuB,eACzB,EAAE,UAAU,GACd;EACF;CACF,GAAG,CAAC,CAAW,CAAC,GAcZ,IACK,OAIF,kBAAC,GAAD;EAAiC;EAAO,WAhBpB,MAA0B;GACnD,GAAgB,OACV,GAAU,aAAa,EAAS,UAAU,SAAS,IACrD,EAAK,IAAI,GAAyB,KAAK,UAAU,CAAQ,CAAC,IAE1D,EAAK,OAAO,CAAuB,GAE9B,EACR;EACH;CAO6E,CAAA;AAC/E;AAOA,SAAwB,EAAkB,GAA+B;CACvE,OACE,kBAAC,GAAD;EAAyB,GAAI;YAC3B,kBAAC,GAAD,CAAqB,CAAA;CACE,CAAA;AAE7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"SynapseTableRenderers.d.ts","sourceRoot":"","sources":["../../../src/components/SynapseTable/SynapseTableRenderers.tsx"],"names":[],"mappings":"AAEA,OAAO,EAIL,GAAG,EACJ,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,WAAW,EAEX,aAAa,EACd,MAAM,uBAAuB,CAAA;AA2D9B,eAAO,MAAM,kBAAkB,gEAQ7B,CAAA;AAuBF,eAAO,MAAM,uBAAuB,gEAQlC,CAAA;AAiBF,eAAO,MAAM,oBAAoB,gEAQ/B,CAAA;AAyEF,eAAO,MAAM,YAAY,gEAQvB,CAAA;AAEF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC,2CAiEzC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC,2CAyCnE"}
1
+ {"version":3,"file":"SynapseTableRenderers.d.ts","sourceRoot":"","sources":["../../../src/components/SynapseTable/SynapseTableRenderers.tsx"],"names":[],"mappings":"AAEA,OAAO,EAIL,GAAG,EACJ,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,WAAW,EAEX,aAAa,EACd,MAAM,uBAAuB,CAAA;AA2D9B,eAAO,MAAM,kBAAkB,gEAQ7B,CAAA;AAuBF,eAAO,MAAM,uBAAuB,gEAQlC,CAAA;AAiBF,eAAO,MAAM,oBAAoB,gEAQ/B,CAAA;AAyEF,eAAO,MAAM,YAAY,gEAQvB,CAAA;AAEF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC,2CAgEzC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC,2CAyCnE"}