synapse-react-client 4.0.9 → 4.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/SWC.index.d.ts +1 -0
- package/dist/SWC.index.d.ts.map +1 -1
- package/dist/SWC.index.js +2 -1
- package/dist/SWC.index.js.map +1 -1
- package/dist/aridhia-queries/aridhiaTokenExchange.js.map +1 -1
- package/dist/aridhia-queries/useGetAridhiaRequests.js.map +1 -1
- package/dist/assets/icons/CloudWarning.d.ts +5 -0
- package/dist/assets/icons/CloudWarning.d.ts.map +1 -0
- package/dist/assets/icons/CloudWarning.js +47 -0
- package/dist/assets/icons/CloudWarning.js.map +1 -0
- package/dist/assets/icons/TasksIcon.d.ts.map +1 -1
- package/dist/assets/icons/TasksIcon.js +6 -10
- package/dist/assets/icons/TasksIcon.js.map +1 -1
- package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.d.ts.map +1 -1
- package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.js +69 -63
- package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.js.map +1 -1
- package/dist/components/AccessRequirementList/AccessApprovalCheckMark.js.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementList.js.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementListUtils.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/DataAccessRequestAccessorsEditor.js.map +1 -1
- package/dist/components/AccessRequirementList/RequirementItem/SelfSignAccessRequirementItem.js.map +1 -1
- package/dist/components/AccessRequirementRelatedProjectsList/AccessRequirementRelatedProjectsList.js.map +1 -1
- package/dist/components/AccessTokenPage/AccessTokenCard/AccessTokenCard.js.map +1 -1
- package/dist/components/AcknowledgementsPage/StudyAcknowledgements.js.map +1 -1
- package/dist/components/AclEditor/PermissionLevelMenu.js.map +1 -1
- package/dist/components/AclEditor/ResourceAccessAndUserGroupHeader.js.map +1 -1
- package/dist/components/AclEditor/useSortResourceAccessList.js.map +1 -1
- package/dist/components/AclEditor/useUpdateAcl.js.map +1 -1
- package/dist/components/Aridhia/AridhiaAccessStatus.js.map +1 -1
- package/dist/components/Authentication/AuthenticationMethodSelection.d.ts.map +1 -1
- package/dist/components/Authentication/AuthenticationMethodSelection.js +38 -37
- package/dist/components/Authentication/AuthenticationMethodSelection.js.map +1 -1
- package/dist/components/Authentication/Constants.d.ts +1 -0
- package/dist/components/Authentication/Constants.d.ts.map +1 -1
- package/dist/components/Authentication/Constants.js +2 -2
- package/dist/components/Authentication/Constants.js.map +1 -1
- package/dist/components/Authentication/LastLoginInfo.js.map +1 -1
- package/dist/components/Authentication/RecoveryCodeForm.js.map +1 -1
- package/dist/components/Authentication/RecoveryCodeGrid.js.map +1 -1
- package/dist/components/Authentication/RegenerateBackupCodesWarning.js.map +1 -1
- package/dist/components/Authentication/Reset2FAWarning.js.map +1 -1
- package/dist/components/Authentication/StandaloneLoginForm.js +1 -1
- package/dist/components/Authentication/TwoFactorBackupCodes.js.map +1 -1
- package/dist/components/Authentication/TwoFactorEnrollmentForm.d.ts.map +1 -1
- package/dist/components/Authentication/TwoFactorEnrollmentForm.js +2 -1
- package/dist/components/Authentication/TwoFactorEnrollmentForm.js.map +1 -1
- package/dist/components/BasePortalCard/ColorfulPortalCardWithChips/ColorfulPortalCardWithChips.js.map +1 -1
- package/dist/components/CardContainer/CardContainer.js.map +1 -1
- package/dist/components/CardDeck/CardDeck.Mobile.js.map +1 -1
- package/dist/components/CardDeck/TableQueryCardDeck.js.map +1 -1
- package/dist/components/CertificationQuiz/CertificationQuiz.js.map +1 -1
- package/dist/components/ChallengeDataDownload/ChallengeDataDownload.js.map +1 -1
- package/dist/components/ChallengeSubmission/ChallengeSubmission.js.map +1 -1
- package/dist/components/ChallengeSubmission/ChallengeSubmissionStepper.js.map +1 -1
- package/dist/components/ChallengeSubmission/EvaluationQueueCurrentRoundInfo.js.map +1 -1
- package/dist/components/ChallengeSubmission/EvaluationQueueList.js.map +1 -1
- package/dist/components/ChallengeSubmission/SubmissionDirectoryList.d.ts.map +1 -1
- package/dist/components/ChallengeSubmission/SubmissionDirectoryList.js +143 -140
- package/dist/components/ChallengeSubmission/SubmissionDirectoryList.js.map +1 -1
- package/dist/components/ChallengeTeamWizard/ChallengeTeamWizard.js.map +1 -1
- package/dist/components/ChallengeTeamWizard/CreateChallengeTeam.js.map +1 -1
- package/dist/components/ChangePassword/ChangePassword.js.map +1 -1
- package/dist/components/ChangePassword/ChangePasswordWithToken.js.map +1 -1
- package/dist/components/ChangePassword/useChangePasswordFormState.js +1 -1
- package/dist/components/ChangePassword/useChangePasswordFormState.js.map +1 -1
- package/dist/components/CitationPopover/CitationPopoverContent.js.map +1 -1
- package/dist/components/ColumnFilter/ColumnFilter.js.map +1 -1
- package/dist/components/ComponentCollapse.js.map +1 -1
- package/dist/components/CookiesNotification/CookiesNotification.js.map +1 -1
- package/dist/components/CreateProjectModal/CreateProjectModal.js.map +1 -1
- package/dist/components/CreateTableViewWizard/CreateTableViewWizardUtils.js.map +1 -1
- package/dist/components/DataGrid/DataGrid.d.ts +0 -1
- package/dist/components/DataGrid/DataGrid.d.ts.map +1 -1
- package/dist/components/DataGrid/DataGrid.js +72 -72
- package/dist/components/DataGrid/DataGrid.js.map +1 -1
- package/dist/components/DataGrid/DataGridWebSocket.d.ts +4 -0
- package/dist/components/DataGrid/DataGridWebSocket.d.ts.map +1 -1
- package/dist/components/DataGrid/DataGridWebSocket.js +9 -8
- package/dist/components/DataGrid/DataGridWebSocket.js.map +1 -1
- package/dist/components/DataGrid/SynapseGrid.d.ts.map +1 -1
- package/dist/components/DataGrid/SynapseGrid.js +326 -268
- package/dist/components/DataGrid/SynapseGrid.js.map +1 -1
- package/dist/components/DataGrid/columns/AutocompleteColumn.d.ts +2 -0
- package/dist/components/DataGrid/columns/AutocompleteColumn.d.ts.map +1 -1
- package/dist/components/DataGrid/columns/AutocompleteColumn.js +124 -67
- package/dist/components/DataGrid/columns/AutocompleteColumn.js.map +1 -1
- package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.d.ts +2 -1
- package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.d.ts.map +1 -1
- package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.js +126 -122
- package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.js.map +1 -1
- package/dist/components/DataGrid/columns/useGridAutocompleteState.d.ts +58 -0
- package/dist/components/DataGrid/columns/useGridAutocompleteState.d.ts.map +1 -0
- package/dist/components/DataGrid/columns/useGridAutocompleteState.js +52 -0
- package/dist/components/DataGrid/columns/useGridAutocompleteState.js.map +1 -0
- package/dist/components/DataGrid/components/ValidationAlert.d.ts +5 -2
- package/dist/components/DataGrid/components/ValidationAlert.d.ts.map +1 -1
- package/dist/components/DataGrid/components/ValidationAlert.js +429 -24
- package/dist/components/DataGrid/components/ValidationAlert.js.map +1 -1
- package/dist/components/DataGrid/hooks/useColumnResizeHandles.js.map +1 -1
- package/dist/components/DataGrid/hooks/useGetSchemaForGrid.js.map +1 -1
- package/dist/components/DataGrid/hooks/useGridUndoRedo.js.map +1 -1
- package/dist/components/DataGrid/hooks/useStack.js.map +1 -1
- package/dist/components/DataGrid/useCRDTModelView.js.map +1 -1
- package/dist/components/DataGrid/useDataGridWebsocket.d.ts +7 -0
- package/dist/components/DataGrid/useDataGridWebsocket.d.ts.map +1 -1
- package/dist/components/DataGrid/useDataGridWebsocket.js +16 -2
- package/dist/components/DataGrid/useDataGridWebsocket.js.map +1 -1
- package/dist/components/DataGrid/useInitializeGridConnection.js.map +1 -1
- package/dist/components/DataGrid/useMergeGridWithRecordSet.js.map +1 -1
- package/dist/components/DataGrid/useMergeGridWithSource.js.map +1 -1
- package/dist/components/DataGrid/useMergeGridWithTable.js.map +1 -1
- package/dist/components/DataGrid/utils/DataGridUtils.js.map +1 -1
- package/dist/components/DataGrid/utils/applyModelChange.d.ts +1 -1
- package/dist/components/DataGrid/utils/applyModelChange.d.ts.map +1 -1
- package/dist/components/DataGrid/utils/applyModelChange.js +27 -24
- package/dist/components/DataGrid/utils/applyModelChange.js.map +1 -1
- package/dist/components/DataGrid/utils/columnFactory.d.ts +8 -0
- package/dist/components/DataGrid/utils/columnFactory.d.ts.map +1 -1
- package/dist/components/DataGrid/utils/columnFactory.js +47 -44
- package/dist/components/DataGrid/utils/columnFactory.js.map +1 -1
- package/dist/components/DataGrid/utils/computeReplicaSelectionModel.js.map +1 -1
- package/dist/components/DataGrid/utils/extractColumnValidationMessages.js.map +1 -1
- package/dist/components/DataGrid/utils/getCellClassName.d.ts.map +1 -1
- package/dist/components/DataGrid/utils/getCellClassName.js +8 -8
- package/dist/components/DataGrid/utils/getCellClassName.js.map +1 -1
- package/dist/components/DataGrid/utils/getEmptyValue.d.ts +2 -0
- package/dist/components/DataGrid/utils/getEmptyValue.d.ts.map +1 -0
- package/dist/components/DataGrid/utils/getEmptyValue.js +8 -0
- package/dist/components/DataGrid/utils/getEmptyValue.js.map +1 -0
- package/dist/components/DataGrid/utils/json-rx/JsonRx.js.map +1 -1
- package/dist/components/DataGrid/utils/modelColsToGrid.d.ts.map +1 -1
- package/dist/components/DataGrid/utils/modelColsToGrid.js +2 -1
- package/dist/components/DataGrid/utils/modelColsToGrid.js.map +1 -1
- package/dist/components/DataGrid/utils/modelRowsToGrid.js.map +1 -1
- package/dist/components/DataGrid/utils/parseFreeTextUsingJsonSchemaType.js.map +1 -1
- package/dist/components/DataGrid/utils/schemaAwarePasteValue.d.ts +32 -0
- package/dist/components/DataGrid/utils/schemaAwarePasteValue.d.ts.map +1 -0
- package/dist/components/DataGrid/utils/schemaAwarePasteValue.js +22 -0
- package/dist/components/DataGrid/utils/schemaAwarePasteValue.js.map +1 -0
- package/dist/components/DataGrid/utils/splitPatch.js.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
- package/dist/components/DirectDownload/DirectDownload.js.map +1 -1
- package/dist/components/DirectDownloadButton.js.map +1 -1
- package/dist/components/DownloadCart/CreatePackageV2.js.map +1 -1
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.css +1 -0
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.d.ts.map +1 -1
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.js +199 -132
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.js.map +1 -1
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.js +22 -0
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.js.map +1 -0
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.scss +170 -0
- package/dist/components/DownloadCart/DownloadListActionsRequired.js.map +1 -1
- package/dist/components/DownloadCart/DownloadListTable.js.map +1 -1
- package/dist/components/DownloadCart/fileNameUtils.js.map +1 -1
- package/dist/components/DraggableDialog/DraggableDialog.js.map +1 -1
- package/dist/components/DynamicForm/DynamicFormModal.js.map +1 -1
- package/dist/components/Ecosystem/TableQueryEcosystem.js.map +1 -1
- package/dist/components/EntityAclEditor/EntityAclEditor.d.ts.map +1 -1
- package/dist/components/EntityAclEditor/EntityAclEditor.js +103 -103
- package/dist/components/EntityAclEditor/EntityAclEditor.js.map +1 -1
- package/dist/components/EntityAclEditor/useNotifyNewACLUsers.js.map +1 -1
- package/dist/components/EntityBadgeIcons/EntityBadgeIcons.js.map +1 -1
- package/dist/components/EntityCitation/EntityCitation.js.map +1 -1
- package/dist/components/EntityDownloadButton/EntityDownloadButton.d.ts.map +1 -1
- package/dist/components/EntityDownloadButton/EntityDownloadButton.js +1 -0
- package/dist/components/EntityDownloadButton/EntityDownloadButton.js.map +1 -1
- package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.d.ts.map +1 -1
- package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.js +36 -30
- package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.js.map +1 -1
- package/dist/components/EntityFinder/EntityFinder.js.map +1 -1
- package/dist/components/EntityFinder/VersionSelectionType.js.map +1 -1
- package/dist/components/EntityFinder/details/configurations/EntityChildrenDetails.js.map +1 -1
- package/dist/components/EntityFinder/details/configurations/FavoritesDetails.js.map +1 -1
- package/dist/components/EntityFinder/details/configurations/ProjectListDetails.js.map +1 -1
- package/dist/components/EntityFinder/details/view/DetailsView.js.map +1 -1
- package/dist/components/EntityFinder/tree/EntityTree.js.map +1 -1
- package/dist/components/EntityFinder/tree/VirtualizedTree.js.map +1 -1
- package/dist/components/EntityFinder/useEntitySelection.js.map +1 -1
- package/dist/components/EntityForm/EntityForm.js.map +1 -1
- package/dist/components/EntityHeaderTable/EntityHeaderTable.js.map +1 -1
- package/dist/components/EntityHeaderTable/Filter.js.map +1 -1
- package/dist/components/EntityHeaderTable/useEntityHeaderTableState.js.map +1 -1
- package/dist/components/EntitySubjectsSelector/EntitySubjectsSelector.js.map +1 -1
- package/dist/components/EntityTreeTable/components/IdColumnHeader.js.map +1 -1
- package/dist/components/EntityTreeTable/hooks/useEntityTreeState.js.map +1 -1
- package/dist/components/EntityTreeTable/hooks/useTableColumns.js.map +1 -1
- package/dist/components/EntityTreeTable/hooks/useTableData.js.map +1 -1
- package/dist/components/EntityTreeTable/hooks/useTreeOperationsWithDirectFetch.js.map +1 -1
- package/dist/components/EntityUpload/EntityUpload.js.map +1 -1
- package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.d.ts.map +1 -1
- package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.js +15 -14
- package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.js.map +1 -1
- package/dist/components/ExperimentalMode/ExperimentalMode.js.map +1 -1
- package/dist/components/ExternalFileHandleLink/ExternalFileHandleLink.js.map +1 -1
- package/dist/components/FeaturedDataTabs/FacetPlotsCard.js.map +1 -1
- package/dist/components/FeaturedDataTabs/QueryPerFacetPlotsCard.js.map +1 -1
- package/dist/components/FeaturedDataTabs/SingleQueryFacetPlotsCards.js.map +1 -1
- package/dist/components/FeaturedResearch/FeaturedResearch.js.map +1 -1
- package/dist/components/FeaturedToolsList/FeaturedToolsList.js.map +1 -1
- package/dist/components/FilePreview/FileHandleContentRenderer.js.map +1 -1
- package/dist/components/FilePreview/HtmlPreview/HtmlPreview.js.map +1 -1
- package/dist/components/FilePreview/PreviewRendererType.js.map +1 -1
- package/dist/components/Forum/DiscussionReply.d.ts +1 -0
- package/dist/components/Forum/DiscussionReply.d.ts.map +1 -1
- package/dist/components/Forum/DiscussionReply.js +19 -19
- package/dist/components/Forum/DiscussionReply.js.map +1 -1
- package/dist/components/Forum/DiscussionSearchResult.js.map +1 -1
- package/dist/components/Forum/DiscussionThread.d.ts +1 -0
- package/dist/components/Forum/DiscussionThread.d.ts.map +1 -1
- package/dist/components/Forum/DiscussionThread.js +73 -72
- package/dist/components/Forum/DiscussionThread.js.map +1 -1
- package/dist/components/Forum/ForumTable.js.map +1 -1
- package/dist/components/Forum/ForumThreadEditor.js.map +1 -1
- package/dist/components/FullTextSearch/FullTextSearchUtils.js.map +1 -1
- package/dist/components/GenericCard/BioregistryRules.d.ts.map +1 -1
- package/dist/components/GenericCard/BioregistryRules.js +7 -3
- package/dist/components/GenericCard/BioregistryRules.js.map +1 -1
- package/dist/components/GenericCard/GenericCard.d.ts.map +1 -1
- package/dist/components/GenericCard/GenericCard.js +12 -7
- package/dist/components/GenericCard/GenericCard.js.map +1 -1
- package/dist/components/GenericCard/Linkify.js.map +1 -1
- package/dist/components/GenericCard/SynapseCardLabel.js.map +1 -1
- package/dist/components/GenericCard/TableRowGenericCard.js +105 -105
- package/dist/components/GenericCard/TableRowGenericCard.js.map +1 -1
- package/dist/components/Goals/Goals.Mobile.js.map +1 -1
- package/dist/components/Goals/Goals.js.map +1 -1
- package/dist/components/GoalsV2/GoalsV2.Mobile.js.map +1 -1
- package/dist/components/GoalsV2/GoalsV2.js.map +1 -1
- package/dist/components/GoalsV3/GoalsV3.Mobile.js.map +1 -1
- package/dist/components/GoalsV3/GoalsV3.js.map +1 -1
- package/dist/components/GoogleMap/SynapseUserMarker.js.map +1 -1
- package/dist/components/HasAccess/AccessIcon.js.map +1 -1
- package/dist/components/HasAccess/useHasAccess.js.map +1 -1
- package/dist/components/HeaderCard/HeaderCardV2.js.map +1 -1
- package/dist/components/HeaderCard.d.ts +6 -1
- package/dist/components/HeaderCard.d.ts.map +1 -1
- package/dist/components/HeaderCard.js +107 -76
- package/dist/components/HeaderCard.js.map +1 -1
- package/dist/components/HexGrid/HexGrid.js.map +1 -1
- package/dist/components/IconList.js.map +1 -1
- package/dist/components/IconSvg/IconSvg.d.ts.map +1 -1
- package/dist/components/IconSvg/IconSvg.js +2 -1
- package/dist/components/IconSvg/IconSvg.js.map +1 -1
- package/dist/components/ImageCardGridWithLinks/ImageCardGridWithLinks.js.map +1 -1
- package/dist/components/ImageFromSynapseTable.js.map +1 -1
- package/dist/components/JSONArrayEditor/useParseCsv.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/ArrayFieldDescriptionTemplate.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/ArrayFieldItemTemplate.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/BaseInputTemplate.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/FieldTemplate.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/RJSFInputLabel.js.map +1 -1
- package/dist/components/Markdown/MarkdownGithub.js.map +1 -1
- package/dist/components/Markdown/MarkdownSynapse.js.map +1 -1
- package/dist/components/Markdown/MarkdownUtils.js.map +1 -1
- package/dist/components/Markdown/SynapseWikiContext.js.map +1 -1
- package/dist/components/Markdown/UserMentionModal.js.map +1 -1
- package/dist/components/Markdown/widget/MarkdownProvenanceGraph.js.map +1 -1
- package/dist/components/MissingQueryResultsWarning/MissingQueryResultsWarning.js.map +1 -1
- package/dist/components/ModalDownload/ModalDownload.js.map +1 -1
- package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.d.ts.map +1 -1
- package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.js +45 -39
- package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.js.map +1 -1
- package/dist/components/OAuthClientManagement/OAuthManagement.js.map +1 -1
- package/dist/components/PageProgress/PageProgress.js.map +1 -1
- package/dist/components/Plot/DotPlot.js.map +1 -1
- package/dist/components/Plot/Plot.js.map +1 -1
- package/dist/components/Plot/SynapsePlot.js.map +1 -1
- package/dist/components/Plot/ThemesPlot.js.map +1 -1
- package/dist/components/Plot/UpsetPlot.js.map +1 -1
- package/dist/components/PortalAclEditor/PortalAclEditor.d.ts.map +1 -1
- package/dist/components/PortalAclEditor/PortalAclEditor.js +43 -41
- package/dist/components/PortalAclEditor/PortalAclEditor.js.map +1 -1
- package/dist/components/PortalFeaturedPartners/PortalFeaturedPartners.js.map +1 -1
- package/dist/components/PortalList/CreatePortalModal.js.map +1 -1
- package/dist/components/ProgrammaticInstructionsModal/ProgrammaticInstructionsModal.js.map +1 -1
- package/dist/components/ProgrammaticTableDownload/ProgrammaticTableDownload.js.map +1 -1
- package/dist/components/Programs/Programs.Mobile.js.map +1 -1
- package/dist/components/Programs/Programs.js.map +1 -1
- package/dist/components/ProvenanceGraph/ProvenanceExternalIcon.js.map +1 -1
- package/dist/components/ProvenanceGraph/ProvenanceGraph.js.map +1 -1
- package/dist/components/ProvenanceGraph/ProvenanceGraphUtils.js.map +1 -1
- package/dist/components/ProvenanceGraph/ProvenanceUtils.js.map +1 -1
- package/dist/components/QueryCount/QueryCount.js.map +1 -1
- package/dist/components/QueryCountButton/QueryCountButton.js.map +1 -1
- package/dist/components/QueryVisualizationWrapper/QueryVisualizationWrapper.js.map +1 -1
- package/dist/components/QueryWrapper/QueryWrapper.js.map +1 -1
- package/dist/components/QueryWrapper/TableQueryUseQueryOptions.js.map +1 -1
- package/dist/components/QueryWrapper/TableRowSelectionState.js.map +1 -1
- package/dist/components/QueryWrapper/generateEncodedPathAndQueryForSelectedFacetURL.js.map +1 -1
- package/dist/components/QueryWrapper/useGetQueryMetadata.js.map +1 -1
- package/dist/components/QueryWrapperErrorBoundary.js.map +1 -1
- package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.js.map +1 -1
- package/dist/components/QueryWrapperPlotNav/UseRowSet.js.map +1 -1
- package/dist/components/RecentPublicationsGrid/RecentPublicationsGrid.js.map +1 -1
- package/dist/components/ReleaseCard/ReleaseCardUtils.js.map +1 -1
- package/dist/components/ResizableContainer/hooks/useResizable.js.map +1 -1
- package/dist/components/Resources/Resources.Mobile.js.map +1 -1
- package/dist/components/Resources/Resources.js.map +1 -1
- package/dist/components/RowDataTable/RowDataTableWithQuery.js.map +1 -1
- package/dist/components/SageResourcesPopover/SageResourcesPopover.js.map +1 -1
- package/dist/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.js.map +1 -1
- package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.js.map +1 -1
- package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js.map +1 -1
- package/dist/components/SmartLink/SmartButton.js.map +1 -1
- package/dist/components/SmartLink/SmartLink.js.map +1 -1
- package/dist/components/SourceAppImage.js.map +1 -1
- package/dist/components/StandaloneQueryWrapper/StandaloneQueryWrapper.js.map +1 -1
- package/dist/components/StatisticsPlot.js.map +1 -1
- package/dist/components/StorybookComponentWrapper.js.map +1 -1
- package/dist/components/SubsectionRowRenderer/SubsectionRowRenderer.js.map +1 -1
- package/dist/components/SustainabilityScorecard/SustainabilityScorecard.js.map +1 -1
- package/dist/components/SynapseChat/GridAgentChat.js.map +1 -1
- package/dist/components/SynapseChat/SynapseChatInteraction.js.map +1 -1
- package/dist/components/SynapseChat/SynapseChatMessage.js.map +1 -1
- package/dist/components/SynapseChat/extractMessageFromTraceEvent.js.map +1 -1
- package/dist/components/SynapseForm/StepsSideNav.js.map +1 -1
- package/dist/components/SynapseForm/SummaryTable.js.map +1 -1
- package/dist/components/SynapseForm/SynapseForm.js +4 -2
- package/dist/components/SynapseForm/SynapseForm.js.map +1 -1
- package/dist/components/SynapseForm/SynapseFormWrapper.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseByTheNumbersItem.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseFeatureItem.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseHomepageChatSearch.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseHomepageSearch.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseInActionItem.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapsePlans.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseTrendingProjects.js.map +1 -1
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.d.ts +8 -7
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.d.ts.map +1 -1
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.js +173 -164
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.js.map +1 -1
- package/dist/components/SynapsePortalBanners/SynapsePortalBanners.js.map +1 -1
- package/dist/components/SynapseSearchPageResults/SearchFacetPanel/SearchFacetPanel.js.map +1 -1
- package/dist/components/SynapseSearchPageResults/SearchFacetPanel/SearchFacetPanelUtils.js.map +1 -1
- package/dist/components/SynapseSearchPageResults/SynapseSearchPageResults.js.map +1 -1
- package/dist/components/SynapseTable/EntityIDColumnCopyIcon.js.map +1 -1
- package/dist/components/SynapseTable/NoContentPlaceholderType.js.map +1 -1
- package/dist/components/SynapseTable/RowSelection/RowSelectionControls.js.map +1 -1
- package/dist/components/SynapseTable/SynapseTableCell/SynapseTableCell.js.map +1 -1
- package/dist/components/SynapseTable/SynapseTableRenderers.js.map +1 -1
- package/dist/components/SynapseTable/datasets/DatasetItemsEditor.js.map +1 -1
- package/dist/components/SynapseTable/table-top/ColumnSelection.js.map +1 -1
- package/dist/components/SynapseTable/table-top/DownloadOptions.js.map +1 -1
- package/dist/components/SynapseTable/usePrefetchTableData.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/ColumnModelForm.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/ColumnModelFormFields/DefaultValueField.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/ImportTableColumnsButton.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.d.ts +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.d.ts.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaForm.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaFormReducer.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/Validators/ColumnModelValidator.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/Validators/DatetimeSchema.js.map +1 -1
- package/dist/components/TanStackTable/ColumnHeader.d.ts +1 -0
- package/dist/components/TanStackTable/ColumnHeader.d.ts.map +1 -1
- package/dist/components/TanStackTable/ColumnHeader.js +8 -8
- package/dist/components/TanStackTable/ColumnHeader.js.map +1 -1
- package/dist/components/TanStackTable/ColumnHeaderEnumFilter.js.map +1 -1
- package/dist/components/TanStackTable/TableBody.js.map +1 -1
- package/dist/components/TeamSubjectsSelector/TeamSubjectsSelector.js.map +1 -1
- package/dist/components/TextField/TextField.js.map +1 -1
- package/dist/components/TimelinePlot/TimelinePhase.js.map +1 -1
- package/dist/components/TimelinePlot/TimelinePlot.js.map +1 -1
- package/dist/components/TimelinePlot/TimelinePlotSpeciesSelector.js.map +1 -1
- package/dist/components/UserCard/Avatar.js.map +1 -1
- package/dist/components/UserCardList/UserCardList.js.map +1 -1
- package/dist/components/UserCardList/UserCardListGroups/UserCardListGroups.Mobile.js.map +1 -1
- package/dist/components/UserCardList/UserCardListRotate.js.map +1 -1
- package/dist/components/UserOrTeamBadge/useUserOrTeam.js.map +1 -1
- package/dist/components/UserProfileLinks/UserProjects.js.map +1 -1
- package/dist/components/UserSearchBox/UserSearchBox.js.map +1 -1
- package/dist/components/Webhook/WebhookDashboard.js.map +1 -1
- package/dist/components/WikiMarkdownEditor/WikiMarkdownEditor.js.map +1 -1
- package/dist/components/WikiMarkdownEditorButton/WikiMarkdownEditorButton.js.map +1 -1
- package/dist/components/dataaccess/AccessApprovalsTable.js.map +1 -1
- package/dist/components/dataaccess/AccessRequestSubmissionTable.js.map +1 -1
- package/dist/components/dataaccess/SubmissionPage/SubmissionPage.d.ts.map +1 -1
- package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js +157 -148
- package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js.map +1 -1
- package/dist/components/dataaccess/UseAccessRequirementTable.js.map +1 -1
- package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryTable.js.map +1 -1
- package/dist/components/doi/CreateOrUpdateDoiModal.d.ts.map +1 -1
- package/dist/components/doi/CreateOrUpdateDoiModal.js +20 -19
- package/dist/components/doi/CreateOrUpdateDoiModal.js.map +1 -1
- package/dist/components/entity/page/CreatedByModifiedBy.js.map +1 -1
- package/dist/components/entity/page/action_menu/EntityActionMenu.js.map +1 -1
- package/dist/components/entity/page/title_bar/useDataCiteUsage.js.map +1 -1
- package/dist/components/entity/page/title_bar/useGetMentions.js.map +1 -1
- package/dist/components/error/ErrorPage.js.map +1 -1
- package/dist/components/favorites/FavoritesPage.js.map +1 -1
- package/dist/components/file/upload/BasicFileHandleUpload.js.map +1 -1
- package/dist/components/layout/SWCHeader.d.ts +9 -0
- package/dist/components/layout/SWCHeader.d.ts.map +1 -0
- package/dist/components/layout/SWCHeader.js +19 -0
- package/dist/components/layout/SWCHeader.js.map +1 -0
- package/dist/components/layout/SWCPageLayout.d.ts +9 -0
- package/dist/components/layout/SWCPageLayout.d.ts.map +1 -0
- package/dist/components/layout/SWCPageLayout.js +14 -0
- package/dist/components/layout/SWCPageLayout.js.map +1 -0
- package/dist/components/menu/ComplexMenu.js.map +1 -1
- package/dist/components/row_renderers/utils/ChipContainer.js.map +1 -1
- package/dist/components/styled/StyledPopover.js.map +1 -1
- package/dist/components/table/CsvPreview/CsvPreview.js +2 -1
- package/dist/components/table/CsvPreview/CsvPreview.js.map +1 -1
- package/dist/components/table/CsvPreview/CsvPreviewDialog.js.map +1 -1
- package/dist/components/trash/TrashCanList.js.map +1 -1
- package/dist/components/widgets/FileHandleLink.js.map +1 -1
- package/dist/components/widgets/RangeSlider/RangeSlider.js.map +1 -1
- package/dist/components/widgets/SynapseVideo.js.map +1 -1
- package/dist/components/widgets/facet-nav/FacetNavPanel.js.map +1 -1
- package/dist/components/widgets/facet-nav/PlotsContainer.js.map +1 -1
- package/dist/components/widgets/facet-nav/SelectionCriteriaPills.js.map +1 -1
- package/dist/components/widgets/facet-nav/useFacetPlots.js.map +1 -1
- package/dist/components/widgets/query-filter/CombinedRangeFacetFilter.js.map +1 -1
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.js.map +1 -1
- package/dist/components/widgets/query-filter/FacetFilterControls.js.map +1 -1
- package/dist/components/widgets/query-filter/RangeFacetFilter.js.map +1 -1
- package/dist/components/widgets/query-filter/RangeFacetFilterUI.js.map +1 -1
- package/dist/features/curator/GridPage/components/GridPageTitle.d.ts.map +1 -1
- package/dist/features/curator/GridPage/components/GridPageTitle.js +23 -30
- package/dist/features/curator/GridPage/components/GridPageTitle.js.map +1 -1
- package/dist/features/curator/dashboard/CuratorDashboard.d.ts +2 -0
- package/dist/features/curator/dashboard/CuratorDashboard.d.ts.map +1 -0
- package/dist/features/curator/dashboard/CuratorDashboard.js +45 -0
- package/dist/features/curator/dashboard/CuratorDashboard.js.map +1 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.css +1 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.d.ts +9 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.d.ts.map +1 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.js +106 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.js.map +1 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.module.js +12 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.module.js.map +1 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.module.scss +52 -0
- package/dist/features/curator/dashboard/components/NextStepButton.css +1 -0
- package/dist/features/curator/dashboard/components/NextStepButton.d.ts +14 -0
- package/dist/features/curator/dashboard/components/NextStepButton.d.ts.map +1 -0
- package/dist/features/curator/dashboard/components/NextStepButton.js +35 -0
- package/dist/features/curator/dashboard/components/NextStepButton.js.map +1 -0
- package/dist/features/curator/dashboard/components/NextStepButton.module.js +11 -0
- package/dist/features/curator/dashboard/components/NextStepButton.module.js.map +1 -0
- package/dist/features/curator/dashboard/components/NextStepButton.module.scss +57 -0
- package/dist/features/curator/dashboard/components/UserOrTeamChip.css +1 -1
- package/dist/features/curator/dashboard/components/UserOrTeamChip.module.js +1 -1
- package/dist/features/curator/dashboard/components/UserOrTeamChip.module.js.map +1 -1
- package/dist/features/curator/dashboard/components/UserOrTeamChip.module.scss +5 -5
- package/dist/features/curator/dashboard/components/shared.css +1 -0
- package/dist/features/curator/dashboard/components/shared.module.js +5 -0
- package/dist/features/curator/dashboard/components/shared.module.js.map +1 -0
- package/dist/features/curator/dashboard/components/shared.module.scss +8 -0
- package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.d.ts +0 -2
- package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.d.ts.map +1 -1
- package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js +16 -34
- package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js.map +1 -1
- package/dist/features/entity/metadata-task/components/MetadataTasksTableAssigneeCell.d.ts.map +1 -1
- package/dist/features/entity/metadata-task/components/MetadataTasksTableAssigneeCell.js +1 -1
- package/dist/features/entity/metadata-task/components/MetadataTasksTableAssigneeCell.js.map +1 -1
- package/dist/features/entity/metadata-task/hooks/useGetOrCreateGridSessionForSource.js.map +1 -1
- package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask.js.map +1 -1
- package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask_legacy.js.map +1 -1
- package/dist/features/entity/metadata-task/hooks/useMetadataTaskTable.js +1 -1
- package/dist/features/entity/metadata-task/hooks/useMetadataTaskTable.js.map +1 -1
- package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.d.ts +10 -0
- package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.d.ts.map +1 -0
- package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.js +37 -0
- package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.js.map +1 -0
- package/dist/features/entity/metadata-task/utils/constants.d.ts +5 -0
- package/dist/features/entity/metadata-task/utils/constants.d.ts.map +1 -0
- package/dist/features/entity/metadata-task/utils/constants.js +6 -0
- package/dist/features/entity/metadata-task/utils/constants.js.map +1 -0
- package/dist/mocks/challenge/mockChallenge.js.map +1 -1
- package/dist/mocks/entity/mockDataset.js.map +1 -1
- package/dist/mocks/entity/mockDatasetCollection.js.map +1 -1
- package/dist/mocks/entity/mockFileEntity.js.map +1 -1
- package/dist/mocks/entity/mockFileView.js.map +1 -1
- package/dist/mocks/entity/mockGeneratedEntityData.js.map +1 -1
- package/dist/mocks/entity/mockProject.js.map +1 -1
- package/dist/mocks/entity/mockProjectView.js.map +1 -1
- package/dist/mocks/entity/mockRootEntity.js.map +1 -1
- package/dist/mocks/entity/mockTableEntity.js.map +1 -1
- package/dist/mocks/mockWiki.js.map +1 -1
- package/dist/mocks/msw/handlers/asyncJobHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/challengeHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/changePasswordHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/discussionHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/entityHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/fileHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/gridHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/personalAccessTokenHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/subscriptionHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/teamHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/userProfileHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/wikiHandlers.js.map +1 -1
- package/dist/mocks/provenance/mockActivity.js.map +1 -1
- package/dist/mocks/query/mockReleaseCardsTableQueryResultBundle.js.map +1 -1
- package/dist/ror-client/index.js.map +1 -1
- package/dist/style/components/_cards.scss +4 -0
- package/dist/style/components/_data-grid-extra.css +1 -1
- package/dist/style/components/_data-grid-extra.scss +2 -0
- package/dist/style/main.css +1 -1
- package/dist/synapse-client/HttpClient.js.map +1 -1
- package/dist/synapse-client/SynapseClient.js.map +1 -1
- package/dist/synapse-queries/KeyFactory.d.ts +1 -0
- package/dist/synapse-queries/KeyFactory.d.ts.map +1 -1
- package/dist/synapse-queries/KeyFactory.js +3 -0
- package/dist/synapse-queries/KeyFactory.js.map +1 -1
- package/dist/synapse-queries/QueryMatching.test-utils.js.map +1 -1
- package/dist/synapse-queries/auth/useTwoFactorEnrollment.js.map +1 -1
- package/dist/synapse-queries/curation/task/useCurationTask.d.ts +1 -1
- package/dist/synapse-queries/curation/task/useCurationTask.d.ts.map +1 -1
- package/dist/synapse-queries/curation/task/useCurationTask.js +1 -1
- package/dist/synapse-queries/curation/task/useCurationTask.js.map +1 -1
- package/dist/synapse-queries/dataaccess/useRestrictionInformation.js.map +1 -1
- package/dist/synapse-queries/doi/useDOI.js.map +1 -1
- package/dist/synapse-queries/download/useDownloadList.js.map +1 -1
- package/dist/synapse-queries/entity/useEntity.js.map +1 -1
- package/dist/synapse-queries/entity/useEntityBundle.js.map +1 -1
- package/dist/synapse-queries/entity/useExportTableQueryToAnalysisPlatform.js.map +1 -1
- package/dist/synapse-queries/entity/useExportToTerra.js.map +1 -1
- package/dist/synapse-queries/entity/useGetQueryResultBundle.js.map +1 -1
- package/dist/synapse-queries/entity/useSchema.js.map +1 -1
- package/dist/synapse-queries/file/UploadToS3.js.map +1 -1
- package/dist/synapse-queries/file/useDirectUploadToS3.js.map +1 -1
- package/dist/synapse-queries/file/useFiles.js.map +1 -1
- package/dist/synapse-queries/forum/useReply.js.map +1 -1
- package/dist/synapse-queries/forum/useThread.d.ts +1 -0
- package/dist/synapse-queries/forum/useThread.d.ts.map +1 -1
- package/dist/synapse-queries/forum/useThread.js +19 -12
- package/dist/synapse-queries/forum/useThread.js.map +1 -1
- package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts +2 -0
- package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts.map +1 -1
- package/dist/synapse-queries/grid/useEstablishWebsocketConnection.js.map +1 -1
- package/dist/synapse-queries/grid/useExportGrid.js.map +1 -1
- package/dist/synapse-queries/grid/useGridSession.js.map +1 -1
- package/dist/synapse-queries/grid/useImportCsvIntoGrid.js.map +1 -1
- package/dist/synapse-queries/subscription/useSubscription.js.map +1 -1
- package/dist/synapse-queries/table/useGetCsvPreview.js.map +1 -1
- package/dist/synapse-queries/table/useTableUpdateTransaction.js.map +1 -1
- package/dist/synapse-queries/team/useTeamMembers.js.map +1 -1
- package/dist/synapse-queries/user/useGetUserChallenges.js.map +1 -1
- package/dist/synapse-queries/user/useUserBundle.js.map +1 -1
- package/dist/synapse-queries/user/useUserGroupHeader.js.map +1 -1
- package/dist/testutils/ReactQueryMockUtils.js.map +1 -1
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/APIConstants.d.ts +1 -0
- package/dist/utils/APIConstants.d.ts.map +1 -1
- package/dist/utils/APIConstants.js +2 -2
- package/dist/utils/APIConstants.js.map +1 -1
- package/dist/utils/AppUtils/session/ApplicationSessionManager.d.ts.map +1 -1
- package/dist/utils/AppUtils/session/ApplicationSessionManager.js +7 -4
- package/dist/utils/AppUtils/session/ApplicationSessionManager.js.map +1 -1
- package/dist/utils/AppUtils/session/SynapseSessionManager.js.map +1 -1
- package/dist/utils/AppUtils/session/useSessionManager.js.map +1 -1
- package/dist/utils/PermissionLevelToAccessType.js.map +1 -1
- package/dist/utils/challenge/evaluation/EvaluationUtils.js.map +1 -1
- package/dist/utils/context/SynapseContext.js.map +1 -1
- package/dist/utils/functions/AccessControlListUtils.d.ts +4 -0
- package/dist/utils/functions/AccessControlListUtils.d.ts.map +1 -1
- package/dist/utils/functions/AccessControlListUtils.js +12 -1
- package/dist/utils/functions/AccessControlListUtils.js.map +1 -1
- package/dist/utils/functions/EntityTypeUtils.d.ts.map +1 -1
- package/dist/utils/functions/EntityTypeUtils.js +15 -4
- package/dist/utils/functions/EntityTypeUtils.js.map +1 -1
- package/dist/utils/functions/GridApiUtils.js.map +1 -1
- package/dist/utils/functions/QueryFilterUtils.js.map +1 -1
- package/dist/utils/functions/RealmUtils.d.ts +4 -0
- package/dist/utils/functions/RealmUtils.d.ts.map +1 -1
- package/dist/utils/functions/RealmUtils.js +9 -3
- package/dist/utils/functions/RealmUtils.js.map +1 -1
- package/dist/utils/functions/SanitizeHtmlUtils.js.map +1 -1
- package/dist/utils/functions/SanitizeHtmlUtils.test-utils.js.map +1 -1
- package/dist/utils/functions/SqlFunctions.js.map +1 -1
- package/dist/utils/functions/StringUtils.js.map +1 -1
- package/dist/utils/functions/deepLinkingUtils.js.map +1 -1
- package/dist/utils/functions/getDataFromFromStorage.js.map +1 -1
- package/dist/utils/functions/getEndpoint.js.map +1 -1
- package/dist/utils/functions/getUserData.js.map +1 -1
- package/dist/utils/functions/queryUtils.js.map +1 -1
- package/dist/utils/functions/testDownloadSpeed.js.map +1 -1
- package/dist/utils/hooks/useConfirmItems.js.map +1 -1
- package/dist/utils/hooks/useCookiePreferences.js.map +1 -1
- package/dist/utils/hooks/useCreateShortUrl.js.map +1 -1
- package/dist/utils/hooks/useDetectSSOCode.js.map +1 -1
- package/dist/utils/hooks/useDirectDownloadHandler.js.map +1 -1
- package/dist/utils/hooks/useGetGoalData.js.map +1 -1
- package/dist/utils/hooks/useGetInfoFromIds.js.map +1 -1
- package/dist/utils/hooks/useImageUrlUtils.js.map +1 -1
- package/dist/utils/hooks/useImmutableTableQuery/useImmutableTableQuery.js.map +1 -1
- package/dist/utils/hooks/useImmutableTableQuery/useTableQueryReducer.js.map +1 -1
- package/dist/utils/hooks/useIsBot.js.map +1 -1
- package/dist/utils/hooks/useListState.js.map +1 -1
- package/dist/utils/hooks/useLogin.d.ts.map +1 -1
- package/dist/utils/hooks/useLogin.js +53 -52
- package/dist/utils/hooks/useLogin.js.map +1 -1
- package/dist/utils/hooks/useMutuallyExclusiveState.js.map +1 -1
- package/dist/utils/hooks/useOverlay.js.map +1 -1
- package/dist/utils/hooks/usePreFetchResource.js.map +1 -1
- package/dist/utils/hooks/useQuerySearchParam.js.map +1 -1
- package/dist/utils/hooks/useScrollFadeTransition.js.map +1 -1
- package/dist/utils/hooks/useSet.js.map +1 -1
- package/dist/utils/hooks/useSourceAppConfigs.js.map +1 -1
- package/dist/utils/hooks/useTableImageUrl.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/useCreatePathsAndGetParentId.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/useLinkFileEntityToURL.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/usePrepareFileEntityUpload.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/useTrackFileUploads.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/useUploadFileEntities.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/useUploadFiles.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/willUploadsExceedStorageLimit.js.map +1 -1
- package/dist/utils/html/TargetEnum.js.map +1 -1
- package/dist/utils/jsonschema/SchemaAnnotationUtils.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityTree.js","names":[],"sources":["../../../../src/components/EntityFinder/tree/EntityTree.tsx"],"sourcesContent":["import {\n useGetEntityHeader,\n useGetEntityPath,\n useGetFavorites,\n useGetProjectsInfinite,\n} from '@/synapse-queries'\nimport useGetEntityBundle from '@/synapse-queries/entity/useEntityBundle'\nimport { convertToEntityType } from '@/utils/functions/EntityTypeUtils'\nimport { SYNAPSE_ENTITY_ID_REGEX } from '@/utils/functions/RegularExpressions'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport {\n ALL_ENTITY_BUNDLE_FIELDS,\n EntityPath,\n Reference,\n} from '@sage-bionetworks/synapse-types'\nimport {\n Dispatch,\n SetStateAction,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react'\nimport DropdownSelect from '../../DropdownSelect/DropdownSelect'\nimport { SynapseSpinner } from '../../LoadingScreen/LoadingScreen'\nimport { displayToast } from '../../ToastMessage/ToastMessage'\nimport { BreadcrumbItem } from '../Breadcrumbs'\nimport { toEntityHeader } from '../details/configurations/ProjectListDetails'\nimport {\n EntityDetailsListDataConfiguration,\n EntityDetailsListDataConfigurationType,\n} from '../details/EntityDetailsList'\nimport { EntityFinderHeader } from '../EntityFinderHeader'\nimport { EntitySelectionMapType } from '../useEntitySelection'\nimport {\n EntityTreeNodeType,\n RootNodeConfiguration,\n VirtualizedTree,\n} from './VirtualizedTree'\n\nconst isEntityIdInPath = (entityId: string, path: EntityPath): boolean => {\n for (const eh of path.path) {\n if (entityId === eh.id) {\n return true\n }\n }\n return false\n}\n\nexport enum FinderScope {\n CURRENT_PROJECT = 'Current Project',\n ALL_PROJECTS = 'All Projects',\n CREATED_BY_ME = 'Projects Created By Me',\n FAVORITES = 'My Favorites',\n}\n\nfunction getScopeOptionDefaultContainer(\n scope: FinderScope,\n): EntityTreeContainer {\n switch (scope) {\n case FinderScope.FAVORITES:\n return 'root'\n case FinderScope.CURRENT_PROJECT:\n case FinderScope.ALL_PROJECTS:\n case FinderScope.CREATED_BY_ME:\n return null\n }\n}\n\nfunction getScopeOptionNodeName(scope: FinderScope): string {\n switch (scope) {\n case FinderScope.CURRENT_PROJECT:\n case FinderScope.ALL_PROJECTS:\n case FinderScope.CREATED_BY_ME:\n return 'Projects'\n case FinderScope.FAVORITES:\n return 'Favorites'\n }\n}\n\n/**\n * Indicates which container is selected when the tree is used to control another component. If 'root', then the top-level entities\n * should be shown in the other component. If a synID, then that entity's children should be shown in the other component. If null,\n * then a selection has not been made.\n */\nexport type EntityTreeContainer = string | 'root' | null\n\n// if the first item is selected (matching the dropdown), then output a configuration. otherwise, output a synId\nexport type EntityTreeProps = {\n initialScope?: FinderScope\n /** To show the current project, projectId must be defined */\n projectId?: string\n initialContainer: EntityTreeContainer\n currentContainer: EntityTreeContainer\n setCurrentContainer: Dispatch<SetStateAction<EntityTreeContainer>>\n showDropdown: boolean\n selectedEntities: EntitySelectionMapType\n visibleTypes?: EntityType[]\n toggleSelection?: (entity: Reference) => void\n setDetailsViewConfiguration?: (\n configuration: EntityDetailsListDataConfiguration,\n ) => void\n setBreadcrumbItems?: (items: BreadcrumbItem[]) => void\n /** Determines whether to show the root node corresponding to the selected scope */\n showScopeAsRootNode?: boolean\n treeNodeType: EntityTreeNodeType\n /** The entity types that may be selected. */\n selectableTypes: EntityType[]\n hideScopeSelector?: boolean\n}\n\n/**\n * The TreeView displays a user's entities hierarchically, allowing a user to quickly dive into an entity tree.\n *\n * The tree view currently can only be used to drive a DetailsView using the `setDetailsViewConfiguration` property.\n */\nexport function EntityTree(props: EntityTreeProps) {\n const {\n initialScope = FinderScope.CURRENT_PROJECT,\n projectId,\n initialContainer = null,\n currentContainer,\n setCurrentContainer,\n visibleTypes = [EntityType.project, EntityType.folder],\n toggleSelection,\n selectedEntities,\n setDetailsViewConfiguration,\n setBreadcrumbItems,\n showScopeAsRootNode = true,\n treeNodeType,\n selectableTypes,\n hideScopeSelector = false,\n } = props\n\n const DEFAULT_CONFIGURATION: EntityDetailsListDataConfiguration = {\n type: EntityDetailsListDataConfigurationType.PROMPT,\n }\n\n const [scope, setScope] = useState(initialScope)\n\n useEffect(() => {\n if (setDetailsViewConfiguration) {\n setDetailsViewConfiguration(DEFAULT_CONFIGURATION)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const setSelectedId = useCallback(\n (entityId: string) => {\n if (toggleSelection) {\n toggleSelection({ targetId: entityId })\n }\n setCurrentContainer(entityId)\n },\n [setCurrentContainer, toggleSelection],\n )\n\n // For these scopes, use the `useGetProjectsInfinite` hook\n const useProjectData =\n scope === FinderScope.ALL_PROJECTS || scope === FinderScope.CREATED_BY_ME\n\n const {\n data: projectData,\n fetchNextPage: fetchNextPageProjects,\n hasNextPage: hasNextPageProjects,\n isLoading: isLoadingProjects,\n } = useGetProjectsInfinite(\n scope === FinderScope.CREATED_BY_ME\n ? { filter: 'CREATED', sort: 'PROJECT_NAME', sortDirection: 'ASC' }\n : { sort: 'PROJECT_NAME', sortDirection: 'ASC' },\n {\n enabled: useProjectData,\n // Don't refetch the projects. Updating the entity headers will drop all the children that VirtualizedTree has fetched\n refetchInterval: Infinity,\n },\n )\n\n const { data: currentContainerBundle, isSuccess: isSuccessBundle } =\n useGetEntityBundle(currentContainer!, undefined, ALL_ENTITY_BUNDLE_FIELDS, {\n enabled: !!currentContainer && currentContainer !== 'root',\n throwOnError: true,\n })\n\n const { data: favorites, isLoading: isLoadingFavorites } = useGetFavorites(\n 'NAME',\n 'ASC',\n {\n select: data =>\n data.filter(eh => visibleTypes.includes(convertToEntityType(eh.type))),\n // Don't refetch the projects. Updating the entity headers will drop all the children that VirtualizedTree has fetched\n refetchInterval: Infinity,\n throwOnError: true,\n },\n )\n\n const { data: initialContainerPath } = useGetEntityPath(initialContainer!, {\n enabled: !!(\n projectId &&\n initialContainer &&\n initialContainer.match(SYNAPSE_ENTITY_ID_REGEX)\n ),\n refetchInterval: Infinity,\n throwOnError: true,\n })\n\n const { data: projectHeader, isLoading: isLoadingProjectHeader } =\n useGetEntityHeader(projectId, undefined, {\n enabled: !!(projectId ?? initialContainerPath?.path[1]?.id),\n refetchInterval: Infinity,\n })\n\n useEffect(() => {\n if (\n FinderScope.CURRENT_PROJECT === scope &&\n projectId &&\n !isLoadingProjectHeader &&\n !projectHeader\n ) {\n // The header wasn't returned, so the user doesn't have access to the current project\n // Let's change the scope to something else\n displayToast(\n `You don't have access to the current project (${projectId}).`,\n 'warning',\n )\n setScope(FinderScope.CREATED_BY_ME)\n }\n }, [isLoadingProjectHeader, projectHeader, projectId, scope])\n\n // Populates the first level of entities in the tree view\n const {\n topLevelEntities,\n isLoading,\n }: {\n topLevelEntities: Pick<EntityFinderHeader, 'name' | 'id' | 'type'>[]\n isLoading: boolean\n } = useMemo(() => {\n let topLevelEntities: Pick<EntityFinderHeader, 'name' | 'id' | 'type'>[] =\n []\n let isLoading: boolean = false\n switch (scope) {\n case FinderScope.ALL_PROJECTS:\n case FinderScope.CREATED_BY_ME:\n if (projectData) {\n topLevelEntities = projectData.pages\n .flatMap(page => page.results)\n .map(toEntityHeader)\n }\n isLoading = isLoadingProjects\n break\n case FinderScope.FAVORITES: {\n topLevelEntities = favorites ?? []\n isLoading = isLoadingFavorites\n break\n }\n case FinderScope.CURRENT_PROJECT:\n if (projectHeader) {\n // use projectHeader as topLevelEntities\n topLevelEntities = [projectHeader]\n isLoading = isLoadingProjectHeader\n }\n break\n default:\n throw new Error('No scope selected')\n }\n return { topLevelEntities, isLoading }\n }, [\n favorites,\n isLoadingFavorites,\n isLoadingProjectHeader,\n isLoadingProjects,\n projectData,\n projectHeader,\n scope,\n ])\n\n // Creates the configuration for the details view and invokes the callback\n useEffect(() => {\n if (setDetailsViewConfiguration || setBreadcrumbItems) {\n let detailsViewConfig: EntityDetailsListDataConfiguration\n let breadcrumbItems: BreadcrumbItem[] = []\n if (currentContainer === null) {\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.PROMPT,\n }\n breadcrumbItems = []\n } else if (currentContainer === 'root') {\n switch (scope) {\n case FinderScope.ALL_PROJECTS:\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.USER_PROJECTS,\n }\n break\n case FinderScope.CREATED_BY_ME:\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.USER_PROJECTS,\n getProjectParams: {\n filter: 'CREATED',\n },\n }\n break\n case FinderScope.CURRENT_PROJECT:\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.HEADER_LIST,\n headerList: topLevelEntities,\n }\n break\n case FinderScope.FAVORITES:\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.USER_FAVORITES,\n }\n break\n }\n breadcrumbItems = [\n {\n name: scope,\n isCurrent: true,\n action: () => {\n setCurrentContainer('root')\n },\n },\n ]\n } else {\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.PARENT_CONTAINER,\n parentContainerId: currentContainer,\n }\n if (currentContainerBundle) {\n breadcrumbItems = [\n {\n name: scope,\n isCurrent: false,\n action: () => {\n setCurrentContainer('root')\n },\n },\n ...currentContainerBundle.path.path\n .slice(1) // Remove the root entity, syn4489\n .map(entity => {\n return {\n name: entity.name ?? entity.id,\n isCurrent: entity.id === currentContainer,\n action: () => {\n setCurrentContainer(entity.id)\n },\n }\n }),\n ]\n }\n }\n if (setDetailsViewConfiguration) {\n setDetailsViewConfiguration(detailsViewConfig)\n }\n if (setBreadcrumbItems) {\n setBreadcrumbItems(breadcrumbItems)\n }\n }\n }, [\n scope,\n currentContainer,\n topLevelEntities,\n setDetailsViewConfiguration,\n setBreadcrumbItems,\n currentContainerBundle,\n isSuccessBundle,\n setCurrentContainer,\n ])\n\n const rootNodeConfiguration: RootNodeConfiguration = useMemo(\n () => ({\n show: showScopeAsRootNode,\n nodeText: getScopeOptionNodeName(scope),\n children: topLevelEntities,\n fetchNextPage: async () => {\n await fetchNextPageProjects()\n },\n hasNextPage: useProjectData && hasNextPageProjects && !isLoadingProjects,\n }),\n [\n showScopeAsRootNode,\n scope,\n topLevelEntities,\n useProjectData,\n hasNextPageProjects,\n isLoadingProjects,\n fetchNextPageProjects,\n ],\n )\n\n const shouldAutoExpand = useCallback(\n (entityId: string) => {\n if (entityId === 'root') {\n return true\n } else {\n return !!(\n scope === FinderScope.CURRENT_PROJECT &&\n initialContainerPath &&\n isEntityIdInPath(entityId, initialContainerPath)\n )\n }\n },\n [scope, initialContainerPath],\n )\n\n const filteredOptions = Object.values(FinderScope).filter(\n scopeOption =>\n !(scopeOption === FinderScope.CURRENT_PROJECT && projectId == null),\n )\n\n const selectedIndex = filteredOptions.indexOf(scope)\n\n return (\n <div\n className={`TreeView ${\n treeNodeType === EntityTreeNodeType.SINGLE_PANE\n ? 'SelectTree'\n : 'BrowseTree'\n }`}\n >\n {!hideScopeSelector && (\n <div className=\"Header\" onClick={e => e.stopPropagation()}>\n <DropdownSelect\n variant={'outlined'}\n options={filteredOptions}\n selectedIndex={selectedIndex}\n setSelectedIndex={index => {\n const selectedScope = filteredOptions[index]\n if (scope !== selectedScope) {\n setScope(selectedScope)\n setCurrentContainer(\n getScopeOptionDefaultContainer(selectedScope),\n )\n }\n }}\n size=\"small\"\n fullWidth\n />\n </div>\n )}\n {isLoading ? (\n <div className=\"Placeholder\">\n <SynapseSpinner size={30} />\n </div>\n ) : (\n <div className=\"Tree\" role=\"tree\">\n <VirtualizedTree\n selected={selectedEntities}\n visibleTypes={visibleTypes}\n autoExpand={shouldAutoExpand}\n rootNodeConfiguration={rootNodeConfiguration}\n treeNodeType={treeNodeType}\n selectableTypes={selectableTypes}\n currentContainer={currentContainer}\n setSelectedId={setSelectedId}\n />\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EntityTree.js","names":[],"sources":["../../../../src/components/EntityFinder/tree/EntityTree.tsx"],"sourcesContent":["import {\n useGetEntityHeader,\n useGetEntityPath,\n useGetFavorites,\n useGetProjectsInfinite,\n} from '@/synapse-queries'\nimport useGetEntityBundle from '@/synapse-queries/entity/useEntityBundle'\nimport { convertToEntityType } from '@/utils/functions/EntityTypeUtils'\nimport { SYNAPSE_ENTITY_ID_REGEX } from '@/utils/functions/RegularExpressions'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport {\n ALL_ENTITY_BUNDLE_FIELDS,\n EntityPath,\n Reference,\n} from '@sage-bionetworks/synapse-types'\nimport {\n Dispatch,\n SetStateAction,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react'\nimport DropdownSelect from '../../DropdownSelect/DropdownSelect'\nimport { SynapseSpinner } from '../../LoadingScreen/LoadingScreen'\nimport { displayToast } from '../../ToastMessage/ToastMessage'\nimport { BreadcrumbItem } from '../Breadcrumbs'\nimport { toEntityHeader } from '../details/configurations/ProjectListDetails'\nimport {\n EntityDetailsListDataConfiguration,\n EntityDetailsListDataConfigurationType,\n} from '../details/EntityDetailsList'\nimport { EntityFinderHeader } from '../EntityFinderHeader'\nimport { EntitySelectionMapType } from '../useEntitySelection'\nimport {\n EntityTreeNodeType,\n RootNodeConfiguration,\n VirtualizedTree,\n} from './VirtualizedTree'\n\nconst isEntityIdInPath = (entityId: string, path: EntityPath): boolean => {\n for (const eh of path.path) {\n if (entityId === eh.id) {\n return true\n }\n }\n return false\n}\n\nexport enum FinderScope {\n CURRENT_PROJECT = 'Current Project',\n ALL_PROJECTS = 'All Projects',\n CREATED_BY_ME = 'Projects Created By Me',\n FAVORITES = 'My Favorites',\n}\n\nfunction getScopeOptionDefaultContainer(\n scope: FinderScope,\n): EntityTreeContainer {\n switch (scope) {\n case FinderScope.FAVORITES:\n return 'root'\n case FinderScope.CURRENT_PROJECT:\n case FinderScope.ALL_PROJECTS:\n case FinderScope.CREATED_BY_ME:\n return null\n }\n}\n\nfunction getScopeOptionNodeName(scope: FinderScope): string {\n switch (scope) {\n case FinderScope.CURRENT_PROJECT:\n case FinderScope.ALL_PROJECTS:\n case FinderScope.CREATED_BY_ME:\n return 'Projects'\n case FinderScope.FAVORITES:\n return 'Favorites'\n }\n}\n\n/**\n * Indicates which container is selected when the tree is used to control another component. If 'root', then the top-level entities\n * should be shown in the other component. If a synID, then that entity's children should be shown in the other component. If null,\n * then a selection has not been made.\n */\nexport type EntityTreeContainer = string | 'root' | null\n\n// if the first item is selected (matching the dropdown), then output a configuration. otherwise, output a synId\nexport type EntityTreeProps = {\n initialScope?: FinderScope\n /** To show the current project, projectId must be defined */\n projectId?: string\n initialContainer: EntityTreeContainer\n currentContainer: EntityTreeContainer\n setCurrentContainer: Dispatch<SetStateAction<EntityTreeContainer>>\n showDropdown: boolean\n selectedEntities: EntitySelectionMapType\n visibleTypes?: EntityType[]\n toggleSelection?: (entity: Reference) => void\n setDetailsViewConfiguration?: (\n configuration: EntityDetailsListDataConfiguration,\n ) => void\n setBreadcrumbItems?: (items: BreadcrumbItem[]) => void\n /** Determines whether to show the root node corresponding to the selected scope */\n showScopeAsRootNode?: boolean\n treeNodeType: EntityTreeNodeType\n /** The entity types that may be selected. */\n selectableTypes: EntityType[]\n hideScopeSelector?: boolean\n}\n\n/**\n * The TreeView displays a user's entities hierarchically, allowing a user to quickly dive into an entity tree.\n *\n * The tree view currently can only be used to drive a DetailsView using the `setDetailsViewConfiguration` property.\n */\nexport function EntityTree(props: EntityTreeProps) {\n const {\n initialScope = FinderScope.CURRENT_PROJECT,\n projectId,\n initialContainer = null,\n currentContainer,\n setCurrentContainer,\n visibleTypes = [EntityType.project, EntityType.folder],\n toggleSelection,\n selectedEntities,\n setDetailsViewConfiguration,\n setBreadcrumbItems,\n showScopeAsRootNode = true,\n treeNodeType,\n selectableTypes,\n hideScopeSelector = false,\n } = props\n\n const DEFAULT_CONFIGURATION: EntityDetailsListDataConfiguration = {\n type: EntityDetailsListDataConfigurationType.PROMPT,\n }\n\n const [scope, setScope] = useState(initialScope)\n\n useEffect(() => {\n if (setDetailsViewConfiguration) {\n setDetailsViewConfiguration(DEFAULT_CONFIGURATION)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const setSelectedId = useCallback(\n (entityId: string) => {\n if (toggleSelection) {\n toggleSelection({ targetId: entityId })\n }\n setCurrentContainer(entityId)\n },\n [setCurrentContainer, toggleSelection],\n )\n\n // For these scopes, use the `useGetProjectsInfinite` hook\n const useProjectData =\n scope === FinderScope.ALL_PROJECTS || scope === FinderScope.CREATED_BY_ME\n\n const {\n data: projectData,\n fetchNextPage: fetchNextPageProjects,\n hasNextPage: hasNextPageProjects,\n isLoading: isLoadingProjects,\n } = useGetProjectsInfinite(\n scope === FinderScope.CREATED_BY_ME\n ? { filter: 'CREATED', sort: 'PROJECT_NAME', sortDirection: 'ASC' }\n : { sort: 'PROJECT_NAME', sortDirection: 'ASC' },\n {\n enabled: useProjectData,\n // Don't refetch the projects. Updating the entity headers will drop all the children that VirtualizedTree has fetched\n refetchInterval: Infinity,\n },\n )\n\n const { data: currentContainerBundle, isSuccess: isSuccessBundle } =\n useGetEntityBundle(currentContainer!, undefined, ALL_ENTITY_BUNDLE_FIELDS, {\n enabled: !!currentContainer && currentContainer !== 'root',\n throwOnError: true,\n })\n\n const { data: favorites, isLoading: isLoadingFavorites } = useGetFavorites(\n 'NAME',\n 'ASC',\n {\n select: data =>\n data.filter(eh => visibleTypes.includes(convertToEntityType(eh.type))),\n // Don't refetch the projects. Updating the entity headers will drop all the children that VirtualizedTree has fetched\n refetchInterval: Infinity,\n throwOnError: true,\n },\n )\n\n const { data: initialContainerPath } = useGetEntityPath(initialContainer!, {\n enabled: !!(\n projectId &&\n initialContainer &&\n initialContainer.match(SYNAPSE_ENTITY_ID_REGEX)\n ),\n refetchInterval: Infinity,\n throwOnError: true,\n })\n\n const { data: projectHeader, isLoading: isLoadingProjectHeader } =\n useGetEntityHeader(projectId, undefined, {\n enabled: !!(projectId ?? initialContainerPath?.path[1]?.id),\n refetchInterval: Infinity,\n })\n\n useEffect(() => {\n if (\n FinderScope.CURRENT_PROJECT === scope &&\n projectId &&\n !isLoadingProjectHeader &&\n !projectHeader\n ) {\n // The header wasn't returned, so the user doesn't have access to the current project\n // Let's change the scope to something else\n displayToast(\n `You don't have access to the current project (${projectId}).`,\n 'warning',\n )\n setScope(FinderScope.CREATED_BY_ME)\n }\n }, [isLoadingProjectHeader, projectHeader, projectId, scope])\n\n // Populates the first level of entities in the tree view\n const {\n topLevelEntities,\n isLoading,\n }: {\n topLevelEntities: Pick<EntityFinderHeader, 'name' | 'id' | 'type'>[]\n isLoading: boolean\n } = useMemo(() => {\n let topLevelEntities: Pick<EntityFinderHeader, 'name' | 'id' | 'type'>[] =\n []\n let isLoading: boolean = false\n switch (scope) {\n case FinderScope.ALL_PROJECTS:\n case FinderScope.CREATED_BY_ME:\n if (projectData) {\n topLevelEntities = projectData.pages\n .flatMap(page => page.results)\n .map(toEntityHeader)\n }\n isLoading = isLoadingProjects\n break\n case FinderScope.FAVORITES: {\n topLevelEntities = favorites ?? []\n isLoading = isLoadingFavorites\n break\n }\n case FinderScope.CURRENT_PROJECT:\n if (projectHeader) {\n // use projectHeader as topLevelEntities\n topLevelEntities = [projectHeader]\n isLoading = isLoadingProjectHeader\n }\n break\n default:\n throw new Error('No scope selected')\n }\n return { topLevelEntities, isLoading }\n }, [\n favorites,\n isLoadingFavorites,\n isLoadingProjectHeader,\n isLoadingProjects,\n projectData,\n projectHeader,\n scope,\n ])\n\n // Creates the configuration for the details view and invokes the callback\n useEffect(() => {\n if (setDetailsViewConfiguration || setBreadcrumbItems) {\n let detailsViewConfig: EntityDetailsListDataConfiguration\n let breadcrumbItems: BreadcrumbItem[] = []\n if (currentContainer === null) {\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.PROMPT,\n }\n breadcrumbItems = []\n } else if (currentContainer === 'root') {\n switch (scope) {\n case FinderScope.ALL_PROJECTS:\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.USER_PROJECTS,\n }\n break\n case FinderScope.CREATED_BY_ME:\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.USER_PROJECTS,\n getProjectParams: {\n filter: 'CREATED',\n },\n }\n break\n case FinderScope.CURRENT_PROJECT:\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.HEADER_LIST,\n headerList: topLevelEntities,\n }\n break\n case FinderScope.FAVORITES:\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.USER_FAVORITES,\n }\n break\n }\n breadcrumbItems = [\n {\n name: scope,\n isCurrent: true,\n action: () => {\n setCurrentContainer('root')\n },\n },\n ]\n } else {\n detailsViewConfig = {\n type: EntityDetailsListDataConfigurationType.PARENT_CONTAINER,\n parentContainerId: currentContainer,\n }\n if (currentContainerBundle) {\n breadcrumbItems = [\n {\n name: scope,\n isCurrent: false,\n action: () => {\n setCurrentContainer('root')\n },\n },\n ...currentContainerBundle.path.path\n .slice(1) // Remove the root entity, syn4489\n .map(entity => {\n return {\n name: entity.name ?? entity.id,\n isCurrent: entity.id === currentContainer,\n action: () => {\n setCurrentContainer(entity.id)\n },\n }\n }),\n ]\n }\n }\n if (setDetailsViewConfiguration) {\n setDetailsViewConfiguration(detailsViewConfig)\n }\n if (setBreadcrumbItems) {\n setBreadcrumbItems(breadcrumbItems)\n }\n }\n }, [\n scope,\n currentContainer,\n topLevelEntities,\n setDetailsViewConfiguration,\n setBreadcrumbItems,\n currentContainerBundle,\n isSuccessBundle,\n setCurrentContainer,\n ])\n\n const rootNodeConfiguration: RootNodeConfiguration = useMemo(\n () => ({\n show: showScopeAsRootNode,\n nodeText: getScopeOptionNodeName(scope),\n children: topLevelEntities,\n fetchNextPage: async () => {\n await fetchNextPageProjects()\n },\n hasNextPage: useProjectData && hasNextPageProjects && !isLoadingProjects,\n }),\n [\n showScopeAsRootNode,\n scope,\n topLevelEntities,\n useProjectData,\n hasNextPageProjects,\n isLoadingProjects,\n fetchNextPageProjects,\n ],\n )\n\n const shouldAutoExpand = useCallback(\n (entityId: string) => {\n if (entityId === 'root') {\n return true\n } else {\n return !!(\n scope === FinderScope.CURRENT_PROJECT &&\n initialContainerPath &&\n isEntityIdInPath(entityId, initialContainerPath)\n )\n }\n },\n [scope, initialContainerPath],\n )\n\n const filteredOptions = Object.values(FinderScope).filter(\n scopeOption =>\n !(scopeOption === FinderScope.CURRENT_PROJECT && projectId == null),\n )\n\n const selectedIndex = filteredOptions.indexOf(scope)\n\n return (\n <div\n className={`TreeView ${\n treeNodeType === EntityTreeNodeType.SINGLE_PANE\n ? 'SelectTree'\n : 'BrowseTree'\n }`}\n >\n {!hideScopeSelector && (\n <div className=\"Header\" onClick={e => e.stopPropagation()}>\n <DropdownSelect\n variant={'outlined'}\n options={filteredOptions}\n selectedIndex={selectedIndex}\n setSelectedIndex={index => {\n const selectedScope = filteredOptions[index]\n if (scope !== selectedScope) {\n setScope(selectedScope)\n setCurrentContainer(\n getScopeOptionDefaultContainer(selectedScope),\n )\n }\n }}\n size=\"small\"\n fullWidth\n />\n </div>\n )}\n {isLoading ? (\n <div className=\"Placeholder\">\n <SynapseSpinner size={30} />\n </div>\n ) : (\n <div className=\"Tree\" role=\"tree\">\n <VirtualizedTree\n selected={selectedEntities}\n visibleTypes={visibleTypes}\n autoExpand={shouldAutoExpand}\n rootNodeConfiguration={rootNodeConfiguration}\n treeNodeType={treeNodeType}\n selectableTypes={selectableTypes}\n currentContainer={currentContainer}\n setSelectedId={setSelectedId}\n />\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualizedTree.js","names":[],"sources":["../../../../src/components/EntityFinder/tree/VirtualizedTree.tsx"],"sourcesContent":["import React from 'react'\nimport { getEntityChildren } from '@/synapse-client'\nimport { useSynapseContext } from '@/utils'\nimport { formatDate } from '@/utils/functions/DateFormatter'\nimport {\n getEntityTypeFromHeader,\n isContainerType,\n} from '@/utils/functions/EntityTypeUtils'\nimport { Writable } from '@/utils/types/Writable'\nimport { ChevronRight, ExpandMore } from '@mui/icons-material'\nimport { Skeleton, Tooltip, Typography } from '@mui/material'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport { EntityChildrenRequest } from '@sage-bionetworks/synapse-types'\nimport { useQueryClient } from '@tanstack/react-query'\nimport dayjs from 'dayjs'\nimport { cloneDeep } from 'lodash-es'\nimport { ReactNode, useCallback, useEffect, useRef, useState } from 'react'\nimport { useInView } from 'react-intersection-observer'\nimport AutoSizer from 'react-virtualized-auto-sizer'\nimport {\n TreeWalker,\n TreeWalkerValue,\n VariableSizeNodeData,\n VariableSizeNodePublicState,\n VariableSizeTree,\n} from 'react-vtree'\nimport { NodeComponentProps } from 'react-vtree/dist/es/Tree'\nimport { EntityBadgeIcons } from '../../EntityBadgeIcons'\nimport { EntityTypeIcon } from '../../EntityIcon'\nimport { SynapseSpinner } from '../../LoadingScreen/LoadingScreen'\nimport { UserBadge } from '../../UserCard/UserBadge'\nimport { EntityFinderHeader } from '../EntityFinderHeader'\nimport { EntitySelectionMapType } from '../useEntitySelection'\n\nexport enum EntityTreeNodeType {\n /** The tree component's appearance and interactions will facilitate selection. Nodes will be larger and styles will indicate primary selection */\n SINGLE_PANE,\n /** The tree component's appearance and interactions will facilitate browsing. Nodes will be smaller and styles will indicate secondary selection */\n DUAL_PANE,\n}\n\ntype NodeChildren = Readonly<{\n /** The node's children. If undefined, then children have not been fetched. */\n children?: EntityHeaderNode[]\n /** The token used to fetch the next page of children for this entity. If this property is nullish and children is defined, then all children have been fetched. */\n childrenNextPageToken?: string | null\n}>\n\nexport type EntityHeaderNode = (\n | EntityFinderHeader\n // Only will have a subset of fields if the node is fetched via the entity path:\n | Pick<EntityFinderHeader, 'id' | 'name' | 'type'>\n) &\n NodeChildren\ntype PaginationNode = { __paginationNode: true }\n\ntype TreeNode = EntityHeaderNode | RootNodeConfiguration | PaginationNode\n\nfunction getTreeNodeType(\n node: TreeNode,\n): 'entityHeader' | 'rootNodeConfiguration' | 'pagination' {\n if ('__paginationNode' in node) {\n return 'pagination'\n }\n return 'id' in node ? 'entityHeader' : 'rootNodeConfiguration'\n}\n\nfunction isPaginationNode(node: TreeNode): node is PaginationNode {\n return getTreeNodeType(node) === 'pagination'\n}\n\nfunction isEntityHeaderNode(node: TreeNode): node is EntityHeaderNode {\n return getTreeNodeType(node) === 'entityHeader'\n}\n\nfunction isRootNodeConfiguration(\n node: TreeNode,\n): node is RootNodeConfiguration {\n return getTreeNodeType(node) === 'rootNodeConfiguration'\n}\n\nfunction hasMoreChildren(node: TreeNode) {\n if (isPaginationNode(node)) {\n return false\n } else if (isRootNodeConfiguration(node)) {\n return node.hasNextPage\n }\n\n return node.children == null || node.childrenNextPageToken != null\n}\n\nfunction isLeafNode(node: TreeNode) {\n if (isPaginationNode(node)) {\n return true\n } else if (isRootNodeConfiguration(node)) {\n return false\n } else {\n return (\n // Entity is not a container\n !isContainerType(getEntityTypeFromHeader(node)) ||\n // OR Children have been fetched (nonnull) and there are 0 children\n (node.children != null && node.children.length === 0)\n )\n }\n}\n\nfunction maybeHasChildren(\n node: TreeNode,\n): node is RootNodeConfiguration | EntityHeaderNode {\n if (isPaginationNode(node)) {\n return false\n } else if (isRootNodeConfiguration(node)) {\n return node.children.length > 0 || node.hasNextPage\n } else {\n return !isLeafNode(node)\n }\n}\n\nexport type TreeData = VariableSizeNodeData &\n Readonly<{\n node: TreeNode\n getNextPageOfChildren: () => Promise<void>\n isLeaf: boolean\n nestingLevel: number\n setSelectedId: (entityId: string) => void\n treeNodeType: EntityTreeNodeType\n isSelected: boolean\n isDisabled: boolean\n }>\n\ntype NodeMeta = Readonly<{\n nestingLevel: number\n node: TreeNode\n}>\n\nexport type RootNodeConfiguration = {\n show: boolean\n nodeText: string\n children: EntityHeaderNode[]\n /** If undefined, no more entities to fetch */\n fetchNextPage: () => Promise<void>\n hasNextPage: boolean\n}\n\n/**\n * Converts a TreeNode and related data into a TreeWalkerValue that react-vtree can use to render the tree.\n * Exported for testing purposes only.\n * @param config\n * @returns\n */\nexport const getNodeData = (config: {\n node: TreeNode\n nestingLevel: number\n getNextPageOfChildren: () => Promise<void>\n setSelectedId: (entityId: string) => void\n treeNodeType: EntityTreeNodeType\n selected: EntitySelectionMapType\n selectableTypes: EntityType[]\n autoExpand: (entityId: string) => boolean\n defaultHeight: number\n currentContainer?: string | 'root' | null\n}): TreeWalkerValue<TreeData, NodeMeta> => {\n const {\n node,\n nestingLevel,\n getNextPageOfChildren,\n setSelectedId,\n treeNodeType,\n selected,\n selectableTypes,\n autoExpand,\n defaultHeight,\n currentContainer,\n } = config\n\n if (isPaginationNode(node)) {\n throw new Error('Cannot create data for a pagination node')\n }\n\n const id = isRootNodeConfiguration(node) ? 'root' : node.id\n\n const isSelected =\n treeNodeType === EntityTreeNodeType.SINGLE_PANE\n ? selected.has(id)\n : currentContainer === id\n const isDisabled =\n !isRootNodeConfiguration(node) &&\n !selectableTypes.includes(getEntityTypeFromHeader(node))\n\n const isOpenByDefault = autoExpand(id)\n /*\n * If the node is open by default and we haven't fetched its children,\n * fetch the first page (otherwise we won't fetch unless re-toggled)\n */\n if (isOpenByDefault && node.children == null && hasMoreChildren(node)) {\n getNextPageOfChildren()\n }\n\n return {\n data: {\n node,\n getNextPageOfChildren,\n id: id,\n isLeaf: isLeafNode(node),\n isOpenByDefault: isOpenByDefault,\n nestingLevel,\n setSelectedId,\n treeNodeType,\n isSelected,\n isDisabled,\n defaultHeight,\n },\n nestingLevel,\n node,\n }\n}\n\n/**\n * Node component for the react-vtree virtualized tree. Exported only for testing purposes\n */\nexport function Node(\n props: NodeComponentProps<TreeData, VariableSizeNodePublicState<TreeData>>,\n) {\n const {\n data: {\n node,\n getNextPageOfChildren,\n isLeaf,\n id,\n nestingLevel,\n setSelectedId,\n treeNodeType,\n isSelected,\n isDisabled,\n },\n isOpen,\n style,\n setOpen,\n } = props\n\n const [isLoading, setLoading] = useState(false)\n\n const nodeText = isEntityHeaderNode(node) ? (\n <span>{node.name}</span>\n ) : isRootNodeConfiguration(node) ? (\n <span>{node.nodeText}</span>\n ) : (\n // Pagination node\n <Skeleton width={100} />\n )\n\n // We only use this for pagination nodes. If the pagination node comes into view, then immediately call `getNextPageOfChildren`\n const { ref, inView } = useInView()\n\n useEffect(() => {\n if (isPaginationNode(node) && inView) {\n getNextPageOfChildren()\n }\n }, [node, inView, getNextPageOfChildren])\n\n const toggleExpand = useCallback(async () => {\n if (hasMoreChildren(node)) {\n setLoading(true)\n await getNextPageOfChildren()\n await setOpen(!isOpen)\n setLoading(false)\n } else {\n await setOpen(!isOpen)\n }\n }, [getNextPageOfChildren, node, isOpen, setOpen])\n\n /**\n * If in the dual-pane view, expand a node when it becomes selected\n */\n useEffect(() => {\n if (\n treeNodeType === EntityTreeNodeType.DUAL_PANE &&\n isSelected &&\n !isOpen\n ) {\n toggleExpand()\n }\n // Intentionally only toggle the expanded state when isSelected changes, otherwise the node cannot be un-expanded\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isSelected])\n\n /**\n * If the height is 0, the node is purposefully hidden. Just render a fragment.\n */\n if ('height' in style && style.height === 0) {\n return <></>\n }\n\n let tooltipContent: ReactNode = ''\n if (isEntityHeaderNode(node)) {\n tooltipContent = (\n <div style={{ textAlign: 'center' }}>\n <Typography\n component=\"span\"\n variant=\"smallText1\"\n sx={{\n display: 'inline',\n }}\n >\n {node.name}\n <br />\n <b>ID: </b>\n {node.id}\n {'modifiedBy' in node && (\n <>\n <br />\n <b>Modified By: </b>\n <UserBadge\n userId={node.modifiedBy.toString()}\n showFullName\n disableLink\n showCardOnHover={false}\n />\n </>\n )}\n {'modifiedOn' in node && (\n <>\n <br />\n <b>Modified On: </b>\n {formatDate(dayjs(node.modifiedOn))}\n </>\n )}\n </Typography>\n </div>\n )\n }\n\n const ExpandIcon = isOpen ? ExpandMore : ChevronRight\n\n return (\n <div\n className={`Node ${\n treeNodeType === EntityTreeNodeType.SINGLE_PANE\n ? 'SelectNode'\n : 'BrowseNode'\n }`}\n aria-selected={isSelected}\n aria-disabled={isDisabled}\n onClick={event => {\n event.stopPropagation()\n if (!isDisabled) {\n setSelectedId(id)\n }\n }}\n style={{\n ...style,\n paddingLeft: `${nestingLevel * 20}px`,\n }}\n >\n {!isLeaf && (\n <>\n {isLoading ? (\n <SynapseSpinner size={10} />\n ) : (\n <button\n aria-label={isOpen ? 'Collapse' : 'Expand'}\n onClick={event => {\n event.stopPropagation()\n toggleExpand()\n }}\n >\n <ExpandIcon className=\"ExpandButton\" />\n </button>\n )}\n </>\n )}\n {treeNodeType === EntityTreeNodeType.SINGLE_PANE && ( // SWC-5592\n <div className=\"EntityIcon\">\n {isEntityHeaderNode(node) && (\n <EntityTypeIcon type={getEntityTypeFromHeader(node)} />\n )}\n </div>\n )}\n\n <Tooltip title={tooltipContent} placement=\"right\">\n <div className=\"EntityName\" ref={ref}>\n {nodeText}\n </div>\n </Tooltip>\n\n {treeNodeType === EntityTreeNodeType.SINGLE_PANE && (\n <EntityBadgeIcons\n entityId={id}\n showHasDiscussionThread={false}\n showHasWiki={false}\n showUnlink={false}\n canOpenModal={false}\n />\n )}\n </div>\n )\n}\n\n/**\n * Extracted from the React component for testing purposes\n * @returns a generator function compatible with react-vtree\n */\nexport function getTreeWalkerFunction(\n rootNode: RootNodeConfiguration,\n setSelectedId: VirtualizedTreeProps['setSelectedId'],\n treeNodeType: VirtualizedTreeProps['treeNodeType'],\n selected: VirtualizedTreeProps['selected'],\n selectableTypes: VirtualizedTreeProps['selectableTypes'],\n autoExpand: VirtualizedTreeProps['autoExpand'],\n itemSize: (index?: number) => number,\n currentContainer: VirtualizedTreeProps['currentContainer'],\n fetchNextPageOfChildren: (node: Writable<EntityHeaderNode>) => Promise<void>,\n): TreeWalker<TreeData, NodeMeta> {\n return function* treeWalker() {\n // Step [1]: Define the root node of our tree.\n yield getNodeData({\n node: rootNode,\n nestingLevel: 0,\n getNextPageOfChildren: rootNode.fetchNextPage,\n setSelectedId,\n treeNodeType,\n selected,\n selectableTypes,\n autoExpand,\n defaultHeight: itemSize(),\n currentContainer,\n })\n\n while (true) {\n // Step [2]: Get the parent node and nesting level, which will be passed into the Generator.next function by react-vtree.\n // Yielding `undefined` indicates to react-vtree that we've finished iterating children from the last time we were in the loop.\n const parentMeta = yield\n\n if (maybeHasChildren(parentMeta.node)) {\n // Step [3]: Yielding all the known children of the parent\n if (parentMeta.node.children) {\n for (let i = 0; i < parentMeta.node.children.length; i++) {\n const childNode = parentMeta.node.children[i]\n\n yield getNodeData({\n node: childNode,\n nestingLevel: parentMeta.nestingLevel + 1,\n getNextPageOfChildren: () => fetchNextPageOfChildren(childNode),\n setSelectedId,\n treeNodeType,\n selected,\n selectableTypes,\n autoExpand,\n defaultHeight: itemSize(),\n currentContainer,\n })\n }\n }\n\n // Step [4] - If the parent node has more children, render a \"pagination node\" that will fetch more children when it comes into view (via intersection observer)\n if (hasMoreChildren(parentMeta.node)) {\n const paginationNode: PaginationNode = {\n __paginationNode: true,\n }\n const paginationTreeWalkerValue: TreeWalkerValue<TreeData, NodeMeta> =\n {\n data: {\n id: parentMeta.data.id + '-pagination',\n node: paginationNode,\n isOpenByDefault: false,\n getNextPageOfChildren: parentMeta.data.getNextPageOfChildren,\n isLeaf: true,\n isSelected: false,\n defaultHeight: itemSize(),\n isDisabled: true,\n nestingLevel: parentMeta.nestingLevel + 1,\n setSelectedId: () => {},\n treeNodeType,\n },\n nestingLevel: parentMeta.nestingLevel + 1,\n node: {\n __paginationNode: true,\n },\n }\n yield paginationTreeWalkerValue\n }\n }\n }\n }\n}\n\nexport type VirtualizedTreeProps = Readonly<{\n treeNodeType: EntityTreeNodeType\n rootNodeConfiguration: RootNodeConfiguration\n setSelectedId: (entityId: string) => void\n selected: EntitySelectionMapType\n /* currentContainer is the container whose contents are shown on in the right pane in dual-pane configuration, and may only be defined when treeNodeType is DUAL_PANE */\n currentContainer?: string | 'root' | null\n autoExpand: (entityId: string) => boolean\n selectableTypes: EntityType[]\n visibleTypes: EntityType[]\n}>\n\n/**\n * Component that utilizes react-vtree to efficiently display a tree of entities. react-vtree utilizes react-window\n * to only render visible nodes, which eliminates performance issues as seen in SWC-5978.\n */\nexport const VirtualizedTree = (\n props: VirtualizedTreeProps,\n): React.ReactNode => {\n const {\n rootNodeConfiguration,\n setSelectedId,\n treeNodeType,\n selected,\n currentContainer,\n selectableTypes,\n visibleTypes,\n autoExpand,\n } = props\n\n const { accessToken, keyFactory } = useSynapseContext()\n const queryClient = useQueryClient()\n\n const [rootNode, setRootNode] = useState<RootNodeConfiguration>(\n rootNodeConfiguration,\n )\n\n const treeInstance = useRef<VariableSizeTree<TreeData>>(null)\n\n useEffect(() => {\n setRootNode(rootNodeConfiguration)\n }, [rootNodeConfiguration, rootNodeConfiguration.children])\n\n // This function is used by VariableSizeTree to identify the size of each node in the tree.\n const itemSize = useCallback(\n (index?: number) => {\n /**\n * We must have a root node, but we don't always want to show it.\n * In those cases, set the height of the root node to 0.\n */\n if (index === 0 && !rootNodeConfiguration.show) {\n return 0\n }\n /**\n * The height of all other nodes in the tree varies depending on the tree node type.\n */\n return treeNodeType === EntityTreeNodeType.DUAL_PANE ? 30 : 60\n },\n [treeNodeType, rootNodeConfiguration.show],\n )\n\n /**\n * Fetches the next page of children for a given node. After fetching the children, this method will make a deep clone of the\n * full tree, including the new data, and updates the state of the component\n */\n const fetchNextPageOfChildren = useCallback(\n // Because we update the root node with a copy at the end of this function, we can write to the node under update.\n async (node: Writable<EntityHeaderNode>) => {\n // Fetch the children of the node\n const entityChildrenRequest: EntityChildrenRequest = {\n parentId: node.id,\n nextPageToken: node.childrenNextPageToken,\n includeTypes: visibleTypes,\n }\n const children = await queryClient.fetchQuery({\n queryKey: keyFactory.getEntityChildrenQueryKey(\n entityChildrenRequest,\n false,\n ),\n queryFn: () => getEntityChildren(entityChildrenRequest, accessToken),\n })\n // Update the node data -- add the children and store the nextPageToken\n if (node.children) {\n for (const newChild of children.page) {\n // SWC-6751 - Prevent adding duplicate entries in case this function is called twice\n if (!node.children.find(child => child.id == newChild.id)) {\n node.children.push(newChild)\n }\n }\n } else {\n node.children = children.page\n }\n node.childrenNextPageToken = children.nextPageToken\n\n // Ensure the node for which we fetched children is expanded/open\n if (treeInstance.current) {\n await treeInstance.current.recomputeTree({\n [node.id]: {\n open: true,\n },\n })\n }\n\n // cloneDeep is required to re-render the tree with the new children\n setRootNode(cloneDeep(rootNode))\n },\n [visibleTypes, queryClient, keyFactory, rootNode, accessToken],\n )\n\n /**\n * treeWalker is a generator function used by react-vtree to generate the tree structure. The tree is re-built when the function is updated,\n * so the dependencies specified in useCallback are important.\n */\n const memoizedTreeWalker = useCallback(\n getTreeWalkerFunction(\n rootNode,\n setSelectedId,\n treeNodeType,\n selected,\n selectableTypes,\n autoExpand,\n itemSize,\n currentContainer,\n fetchNextPageOfChildren,\n ),\n [\n rootNode,\n setSelectedId,\n treeNodeType,\n selected,\n selectableTypes,\n autoExpand,\n currentContainer,\n fetchNextPageOfChildren,\n itemSize,\n ],\n )\n\n return (\n <AutoSizer disableWidth>\n {({ height }: { height: number }) => (\n <VariableSizeTree\n ref={treeInstance}\n treeWalker={memoizedTreeWalker}\n itemSize={itemSize}\n height={height}\n async={true}\n width=\"100%\"\n >\n {Node}\n </VariableSizeTree>\n )}\n </AutoSizer>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VirtualizedTree.js","names":[],"sources":["../../../../src/components/EntityFinder/tree/VirtualizedTree.tsx"],"sourcesContent":["import React from 'react'\nimport { getEntityChildren } from '@/synapse-client'\nimport { useSynapseContext } from '@/utils'\nimport { formatDate } from '@/utils/functions/DateFormatter'\nimport {\n getEntityTypeFromHeader,\n isContainerType,\n} from '@/utils/functions/EntityTypeUtils'\nimport { Writable } from '@/utils/types/Writable'\nimport { ChevronRight, ExpandMore } from '@mui/icons-material'\nimport { Skeleton, Tooltip, Typography } from '@mui/material'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport { EntityChildrenRequest } from '@sage-bionetworks/synapse-types'\nimport { useQueryClient } from '@tanstack/react-query'\nimport dayjs from 'dayjs'\nimport { cloneDeep } from 'lodash-es'\nimport { ReactNode, useCallback, useEffect, useRef, useState } from 'react'\nimport { useInView } from 'react-intersection-observer'\nimport AutoSizer from 'react-virtualized-auto-sizer'\nimport {\n TreeWalker,\n TreeWalkerValue,\n VariableSizeNodeData,\n VariableSizeNodePublicState,\n VariableSizeTree,\n} from 'react-vtree'\nimport { NodeComponentProps } from 'react-vtree/dist/es/Tree'\nimport { EntityBadgeIcons } from '../../EntityBadgeIcons'\nimport { EntityTypeIcon } from '../../EntityIcon'\nimport { SynapseSpinner } from '../../LoadingScreen/LoadingScreen'\nimport { UserBadge } from '../../UserCard/UserBadge'\nimport { EntityFinderHeader } from '../EntityFinderHeader'\nimport { EntitySelectionMapType } from '../useEntitySelection'\n\nexport enum EntityTreeNodeType {\n /** The tree component's appearance and interactions will facilitate selection. Nodes will be larger and styles will indicate primary selection */\n SINGLE_PANE,\n /** The tree component's appearance and interactions will facilitate browsing. Nodes will be smaller and styles will indicate secondary selection */\n DUAL_PANE,\n}\n\ntype NodeChildren = Readonly<{\n /** The node's children. If undefined, then children have not been fetched. */\n children?: EntityHeaderNode[]\n /** The token used to fetch the next page of children for this entity. If this property is nullish and children is defined, then all children have been fetched. */\n childrenNextPageToken?: string | null\n}>\n\nexport type EntityHeaderNode = (\n | EntityFinderHeader\n // Only will have a subset of fields if the node is fetched via the entity path:\n | Pick<EntityFinderHeader, 'id' | 'name' | 'type'>\n) &\n NodeChildren\ntype PaginationNode = { __paginationNode: true }\n\ntype TreeNode = EntityHeaderNode | RootNodeConfiguration | PaginationNode\n\nfunction getTreeNodeType(\n node: TreeNode,\n): 'entityHeader' | 'rootNodeConfiguration' | 'pagination' {\n if ('__paginationNode' in node) {\n return 'pagination'\n }\n return 'id' in node ? 'entityHeader' : 'rootNodeConfiguration'\n}\n\nfunction isPaginationNode(node: TreeNode): node is PaginationNode {\n return getTreeNodeType(node) === 'pagination'\n}\n\nfunction isEntityHeaderNode(node: TreeNode): node is EntityHeaderNode {\n return getTreeNodeType(node) === 'entityHeader'\n}\n\nfunction isRootNodeConfiguration(\n node: TreeNode,\n): node is RootNodeConfiguration {\n return getTreeNodeType(node) === 'rootNodeConfiguration'\n}\n\nfunction hasMoreChildren(node: TreeNode) {\n if (isPaginationNode(node)) {\n return false\n } else if (isRootNodeConfiguration(node)) {\n return node.hasNextPage\n }\n\n return node.children == null || node.childrenNextPageToken != null\n}\n\nfunction isLeafNode(node: TreeNode) {\n if (isPaginationNode(node)) {\n return true\n } else if (isRootNodeConfiguration(node)) {\n return false\n } else {\n return (\n // Entity is not a container\n !isContainerType(getEntityTypeFromHeader(node)) ||\n // OR Children have been fetched (nonnull) and there are 0 children\n (node.children != null && node.children.length === 0)\n )\n }\n}\n\nfunction maybeHasChildren(\n node: TreeNode,\n): node is RootNodeConfiguration | EntityHeaderNode {\n if (isPaginationNode(node)) {\n return false\n } else if (isRootNodeConfiguration(node)) {\n return node.children.length > 0 || node.hasNextPage\n } else {\n return !isLeafNode(node)\n }\n}\n\nexport type TreeData = VariableSizeNodeData &\n Readonly<{\n node: TreeNode\n getNextPageOfChildren: () => Promise<void>\n isLeaf: boolean\n nestingLevel: number\n setSelectedId: (entityId: string) => void\n treeNodeType: EntityTreeNodeType\n isSelected: boolean\n isDisabled: boolean\n }>\n\ntype NodeMeta = Readonly<{\n nestingLevel: number\n node: TreeNode\n}>\n\nexport type RootNodeConfiguration = {\n show: boolean\n nodeText: string\n children: EntityHeaderNode[]\n /** If undefined, no more entities to fetch */\n fetchNextPage: () => Promise<void>\n hasNextPage: boolean\n}\n\n/**\n * Converts a TreeNode and related data into a TreeWalkerValue that react-vtree can use to render the tree.\n * Exported for testing purposes only.\n * @param config\n * @returns\n */\nexport const getNodeData = (config: {\n node: TreeNode\n nestingLevel: number\n getNextPageOfChildren: () => Promise<void>\n setSelectedId: (entityId: string) => void\n treeNodeType: EntityTreeNodeType\n selected: EntitySelectionMapType\n selectableTypes: EntityType[]\n autoExpand: (entityId: string) => boolean\n defaultHeight: number\n currentContainer?: string | 'root' | null\n}): TreeWalkerValue<TreeData, NodeMeta> => {\n const {\n node,\n nestingLevel,\n getNextPageOfChildren,\n setSelectedId,\n treeNodeType,\n selected,\n selectableTypes,\n autoExpand,\n defaultHeight,\n currentContainer,\n } = config\n\n if (isPaginationNode(node)) {\n throw new Error('Cannot create data for a pagination node')\n }\n\n const id = isRootNodeConfiguration(node) ? 'root' : node.id\n\n const isSelected =\n treeNodeType === EntityTreeNodeType.SINGLE_PANE\n ? selected.has(id)\n : currentContainer === id\n const isDisabled =\n !isRootNodeConfiguration(node) &&\n !selectableTypes.includes(getEntityTypeFromHeader(node))\n\n const isOpenByDefault = autoExpand(id)\n /*\n * If the node is open by default and we haven't fetched its children,\n * fetch the first page (otherwise we won't fetch unless re-toggled)\n */\n if (isOpenByDefault && node.children == null && hasMoreChildren(node)) {\n getNextPageOfChildren()\n }\n\n return {\n data: {\n node,\n getNextPageOfChildren,\n id: id,\n isLeaf: isLeafNode(node),\n isOpenByDefault: isOpenByDefault,\n nestingLevel,\n setSelectedId,\n treeNodeType,\n isSelected,\n isDisabled,\n defaultHeight,\n },\n nestingLevel,\n node,\n }\n}\n\n/**\n * Node component for the react-vtree virtualized tree. Exported only for testing purposes\n */\nexport function Node(\n props: NodeComponentProps<TreeData, VariableSizeNodePublicState<TreeData>>,\n) {\n const {\n data: {\n node,\n getNextPageOfChildren,\n isLeaf,\n id,\n nestingLevel,\n setSelectedId,\n treeNodeType,\n isSelected,\n isDisabled,\n },\n isOpen,\n style,\n setOpen,\n } = props\n\n const [isLoading, setLoading] = useState(false)\n\n const nodeText = isEntityHeaderNode(node) ? (\n <span>{node.name}</span>\n ) : isRootNodeConfiguration(node) ? (\n <span>{node.nodeText}</span>\n ) : (\n // Pagination node\n <Skeleton width={100} />\n )\n\n // We only use this for pagination nodes. If the pagination node comes into view, then immediately call `getNextPageOfChildren`\n const { ref, inView } = useInView()\n\n useEffect(() => {\n if (isPaginationNode(node) && inView) {\n getNextPageOfChildren()\n }\n }, [node, inView, getNextPageOfChildren])\n\n const toggleExpand = useCallback(async () => {\n if (hasMoreChildren(node)) {\n setLoading(true)\n await getNextPageOfChildren()\n await setOpen(!isOpen)\n setLoading(false)\n } else {\n await setOpen(!isOpen)\n }\n }, [getNextPageOfChildren, node, isOpen, setOpen])\n\n /**\n * If in the dual-pane view, expand a node when it becomes selected\n */\n useEffect(() => {\n if (\n treeNodeType === EntityTreeNodeType.DUAL_PANE &&\n isSelected &&\n !isOpen\n ) {\n toggleExpand()\n }\n // Intentionally only toggle the expanded state when isSelected changes, otherwise the node cannot be un-expanded\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isSelected])\n\n /**\n * If the height is 0, the node is purposefully hidden. Just render a fragment.\n */\n if ('height' in style && style.height === 0) {\n return <></>\n }\n\n let tooltipContent: ReactNode = ''\n if (isEntityHeaderNode(node)) {\n tooltipContent = (\n <div style={{ textAlign: 'center' }}>\n <Typography\n component=\"span\"\n variant=\"smallText1\"\n sx={{\n display: 'inline',\n }}\n >\n {node.name}\n <br />\n <b>ID: </b>\n {node.id}\n {'modifiedBy' in node && (\n <>\n <br />\n <b>Modified By: </b>\n <UserBadge\n userId={node.modifiedBy.toString()}\n showFullName\n disableLink\n showCardOnHover={false}\n />\n </>\n )}\n {'modifiedOn' in node && (\n <>\n <br />\n <b>Modified On: </b>\n {formatDate(dayjs(node.modifiedOn))}\n </>\n )}\n </Typography>\n </div>\n )\n }\n\n const ExpandIcon = isOpen ? ExpandMore : ChevronRight\n\n return (\n <div\n className={`Node ${\n treeNodeType === EntityTreeNodeType.SINGLE_PANE\n ? 'SelectNode'\n : 'BrowseNode'\n }`}\n aria-selected={isSelected}\n aria-disabled={isDisabled}\n onClick={event => {\n event.stopPropagation()\n if (!isDisabled) {\n setSelectedId(id)\n }\n }}\n style={{\n ...style,\n paddingLeft: `${nestingLevel * 20}px`,\n }}\n >\n {!isLeaf && (\n <>\n {isLoading ? (\n <SynapseSpinner size={10} />\n ) : (\n <button\n aria-label={isOpen ? 'Collapse' : 'Expand'}\n onClick={event => {\n event.stopPropagation()\n toggleExpand()\n }}\n >\n <ExpandIcon className=\"ExpandButton\" />\n </button>\n )}\n </>\n )}\n {treeNodeType === EntityTreeNodeType.SINGLE_PANE && ( // SWC-5592\n <div className=\"EntityIcon\">\n {isEntityHeaderNode(node) && (\n <EntityTypeIcon type={getEntityTypeFromHeader(node)} />\n )}\n </div>\n )}\n\n <Tooltip title={tooltipContent} placement=\"right\">\n <div className=\"EntityName\" ref={ref}>\n {nodeText}\n </div>\n </Tooltip>\n\n {treeNodeType === EntityTreeNodeType.SINGLE_PANE && (\n <EntityBadgeIcons\n entityId={id}\n showHasDiscussionThread={false}\n showHasWiki={false}\n showUnlink={false}\n canOpenModal={false}\n />\n )}\n </div>\n )\n}\n\n/**\n * Extracted from the React component for testing purposes\n * @returns a generator function compatible with react-vtree\n */\nexport function getTreeWalkerFunction(\n rootNode: RootNodeConfiguration,\n setSelectedId: VirtualizedTreeProps['setSelectedId'],\n treeNodeType: VirtualizedTreeProps['treeNodeType'],\n selected: VirtualizedTreeProps['selected'],\n selectableTypes: VirtualizedTreeProps['selectableTypes'],\n autoExpand: VirtualizedTreeProps['autoExpand'],\n itemSize: (index?: number) => number,\n currentContainer: VirtualizedTreeProps['currentContainer'],\n fetchNextPageOfChildren: (node: Writable<EntityHeaderNode>) => Promise<void>,\n): TreeWalker<TreeData, NodeMeta> {\n return function* treeWalker() {\n // Step [1]: Define the root node of our tree.\n yield getNodeData({\n node: rootNode,\n nestingLevel: 0,\n getNextPageOfChildren: rootNode.fetchNextPage,\n setSelectedId,\n treeNodeType,\n selected,\n selectableTypes,\n autoExpand,\n defaultHeight: itemSize(),\n currentContainer,\n })\n\n while (true) {\n // Step [2]: Get the parent node and nesting level, which will be passed into the Generator.next function by react-vtree.\n // Yielding `undefined` indicates to react-vtree that we've finished iterating children from the last time we were in the loop.\n const parentMeta = yield\n\n if (maybeHasChildren(parentMeta.node)) {\n // Step [3]: Yielding all the known children of the parent\n if (parentMeta.node.children) {\n for (let i = 0; i < parentMeta.node.children.length; i++) {\n const childNode = parentMeta.node.children[i]\n\n yield getNodeData({\n node: childNode,\n nestingLevel: parentMeta.nestingLevel + 1,\n getNextPageOfChildren: () => fetchNextPageOfChildren(childNode),\n setSelectedId,\n treeNodeType,\n selected,\n selectableTypes,\n autoExpand,\n defaultHeight: itemSize(),\n currentContainer,\n })\n }\n }\n\n // Step [4] - If the parent node has more children, render a \"pagination node\" that will fetch more children when it comes into view (via intersection observer)\n if (hasMoreChildren(parentMeta.node)) {\n const paginationNode: PaginationNode = {\n __paginationNode: true,\n }\n const paginationTreeWalkerValue: TreeWalkerValue<TreeData, NodeMeta> =\n {\n data: {\n id: parentMeta.data.id + '-pagination',\n node: paginationNode,\n isOpenByDefault: false,\n getNextPageOfChildren: parentMeta.data.getNextPageOfChildren,\n isLeaf: true,\n isSelected: false,\n defaultHeight: itemSize(),\n isDisabled: true,\n nestingLevel: parentMeta.nestingLevel + 1,\n setSelectedId: () => {},\n treeNodeType,\n },\n nestingLevel: parentMeta.nestingLevel + 1,\n node: {\n __paginationNode: true,\n },\n }\n yield paginationTreeWalkerValue\n }\n }\n }\n }\n}\n\nexport type VirtualizedTreeProps = Readonly<{\n treeNodeType: EntityTreeNodeType\n rootNodeConfiguration: RootNodeConfiguration\n setSelectedId: (entityId: string) => void\n selected: EntitySelectionMapType\n /* currentContainer is the container whose contents are shown on in the right pane in dual-pane configuration, and may only be defined when treeNodeType is DUAL_PANE */\n currentContainer?: string | 'root' | null\n autoExpand: (entityId: string) => boolean\n selectableTypes: EntityType[]\n visibleTypes: EntityType[]\n}>\n\n/**\n * Component that utilizes react-vtree to efficiently display a tree of entities. react-vtree utilizes react-window\n * to only render visible nodes, which eliminates performance issues as seen in SWC-5978.\n */\nexport const VirtualizedTree = (\n props: VirtualizedTreeProps,\n): React.ReactNode => {\n const {\n rootNodeConfiguration,\n setSelectedId,\n treeNodeType,\n selected,\n currentContainer,\n selectableTypes,\n visibleTypes,\n autoExpand,\n } = props\n\n const { accessToken, keyFactory } = useSynapseContext()\n const queryClient = useQueryClient()\n\n const [rootNode, setRootNode] = useState<RootNodeConfiguration>(\n rootNodeConfiguration,\n )\n\n const treeInstance = useRef<VariableSizeTree<TreeData>>(null)\n\n useEffect(() => {\n setRootNode(rootNodeConfiguration)\n }, [rootNodeConfiguration, rootNodeConfiguration.children])\n\n // This function is used by VariableSizeTree to identify the size of each node in the tree.\n const itemSize = useCallback(\n (index?: number) => {\n /**\n * We must have a root node, but we don't always want to show it.\n * In those cases, set the height of the root node to 0.\n */\n if (index === 0 && !rootNodeConfiguration.show) {\n return 0\n }\n /**\n * The height of all other nodes in the tree varies depending on the tree node type.\n */\n return treeNodeType === EntityTreeNodeType.DUAL_PANE ? 30 : 60\n },\n [treeNodeType, rootNodeConfiguration.show],\n )\n\n /**\n * Fetches the next page of children for a given node. After fetching the children, this method will make a deep clone of the\n * full tree, including the new data, and updates the state of the component\n */\n const fetchNextPageOfChildren = useCallback(\n // Because we update the root node with a copy at the end of this function, we can write to the node under update.\n async (node: Writable<EntityHeaderNode>) => {\n // Fetch the children of the node\n const entityChildrenRequest: EntityChildrenRequest = {\n parentId: node.id,\n nextPageToken: node.childrenNextPageToken,\n includeTypes: visibleTypes,\n }\n const children = await queryClient.fetchQuery({\n queryKey: keyFactory.getEntityChildrenQueryKey(\n entityChildrenRequest,\n false,\n ),\n queryFn: () => getEntityChildren(entityChildrenRequest, accessToken),\n })\n // Update the node data -- add the children and store the nextPageToken\n if (node.children) {\n for (const newChild of children.page) {\n // SWC-6751 - Prevent adding duplicate entries in case this function is called twice\n if (!node.children.find(child => child.id == newChild.id)) {\n node.children.push(newChild)\n }\n }\n } else {\n node.children = children.page\n }\n node.childrenNextPageToken = children.nextPageToken\n\n // Ensure the node for which we fetched children is expanded/open\n if (treeInstance.current) {\n await treeInstance.current.recomputeTree({\n [node.id]: {\n open: true,\n },\n })\n }\n\n // cloneDeep is required to re-render the tree with the new children\n setRootNode(cloneDeep(rootNode))\n },\n [visibleTypes, queryClient, keyFactory, rootNode, accessToken],\n )\n\n /**\n * treeWalker is a generator function used by react-vtree to generate the tree structure. The tree is re-built when the function is updated,\n * so the dependencies specified in useCallback are important.\n */\n const memoizedTreeWalker = useCallback(\n getTreeWalkerFunction(\n rootNode,\n setSelectedId,\n treeNodeType,\n selected,\n selectableTypes,\n autoExpand,\n itemSize,\n currentContainer,\n fetchNextPageOfChildren,\n ),\n [\n rootNode,\n setSelectedId,\n treeNodeType,\n selected,\n selectableTypes,\n autoExpand,\n currentContainer,\n fetchNextPageOfChildren,\n itemSize,\n ],\n )\n\n return (\n <AutoSizer disableWidth>\n {({ height }: { height: number }) => (\n <VariableSizeTree\n ref={treeInstance}\n treeWalker={memoizedTreeWalker}\n itemSize={itemSize}\n height={height}\n async={true}\n width=\"100%\"\n >\n {Node}\n </VariableSizeTree>\n )}\n </AutoSizer>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEntitySelection.js","names":[],"sources":["../../../src/components/EntityFinder/useEntitySelection.ts"],"sourcesContent":["import { Reference } from '@sage-bionetworks/synapse-types'\nimport { Map } from 'immutable'\nimport { isEqual } from 'lodash-es'\nimport { useCallback, useReducer } from 'react'\n\n// Represents the selection type. The key is the entity ID. Only one version of an entity can be chosen at a time.\nexport type EntitySelectionMapType = Map<string, Reference>\n\nexport function useEntitySelection(\n selectMultiple: boolean,\n initialSelection: EntitySelectionMapType = Map<string, Reference>(),\n) {\n /**\n *\n * @param entity The entity to check selected status\n * @param selected the list of selected entities\n * @returns a boolean array of length equal to entities.length denoting selection status\n */\n const isSelected = useCallback(\n (entity: Reference, selected: EntitySelectionMapType): boolean => {\n const match = selected.get(entity.targetId)\n if (match == null) {\n return false\n }\n return isEqual(entity, match)\n },\n [],\n )\n\n type ReducerAction =\n | {\n type: 'toggleSelection'\n toggledReferences: Reference | Reference[]\n }\n | {\n type: 'setSelection'\n selection: EntitySelectionMapType\n }\n | {\n type: 'setInitialVersion'\n entityId: string\n version: number\n }\n\n /**\n * Given the existing selections and a list of toggled references, return the new list of selections\n * @param currentState\n * @param action\n * @returns\n */\n const entitySelectionReducer = useCallback(\n (\n currentState: EntitySelectionMapType,\n action: ReducerAction,\n ): EntitySelectionMapType => {\n if (action.type === 'setSelection') {\n return action.selection\n }\n if (action.type === 'setInitialVersion') {\n // Safe to call from effects multiple times — only acts the first time\n const existing = currentState.get(action.entityId)\n if (existing != null && existing.targetVersionNumber == null) {\n return currentState.set(action.entityId, {\n targetId: action.entityId,\n targetVersionNumber: action.version,\n })\n }\n // Entity not selected, or already has a version - no-op\n return currentState\n }\n if (action.type === 'toggleSelection') {\n let toggledReferences = action.toggledReferences\n return currentState.withMutations(map => {\n // Note: we currently don't allow selecting two versions of the same entity, so we replace previous selected version with new selected version\n if (!Array.isArray(toggledReferences)) {\n toggledReferences = [toggledReferences]\n }\n toggledReferences.forEach(toggledReference => {\n if (isSelected(toggledReference, currentState)) {\n // remove from selection\n map.delete(toggledReference.targetId)\n } else {\n // add to selection\n if (!selectMultiple) {\n map.clear()\n }\n map.set(toggledReference.targetId, toggledReference)\n }\n })\n })\n }\n return currentState\n },\n [isSelected, selectMultiple],\n )\n\n const [selectedEntities, dispatch] = useReducer(\n entitySelectionReducer,\n initialSelection,\n )\n\n const toggleSelection = useCallback(\n (toggledReferences: Reference | Reference[]) =>\n dispatch({ type: 'toggleSelection', toggledReferences }),\n [],\n )\n\n const setSelection = useCallback(\n (selection: EntitySelectionMapType) =>\n dispatch({ type: 'setSelection', selection }),\n [],\n )\n\n const setInitialVersion = useCallback(\n (entityId: string, version: number) =>\n dispatch({ type: 'setInitialVersion', entityId, version }),\n [],\n )\n\n return {\n selectedEntities,\n toggleSelection,\n setSelection,\n setInitialVersion,\n }\n}\n"],"mappings":";;;;AAQA,SAAgB,EACd,GACA,IAA2C,GAAwB,EACnE;CAOA,IAAM,IAAa,GAChB,GAAmB,MAA8C;EAChE,IAAM,IAAQ,EAAS,IAAI,EAAO,SAAS;AAI3C,SAHI,KAAS,OACJ,KAEF,EAAQ,GAAQ,EAAM;IAE/B,EAAE,CACH,EAqEK,CAAC,GAAkB,KAAY,EA9CN,GAE3B,GACA,MAC2B;AAC3B,MAAI,EAAO,SAAS,eAClB,QAAO,EAAO;AAEhB,MAAI,EAAO,SAAS,qBAAqB;GAEvC,IAAM,IAAW,EAAa,IAAI,EAAO,SAAS;AAQlD,UAPI,KAAY,QAAQ,EAAS,uBAAuB,OAC/C,EAAa,IAAI,EAAO,UAAU;IACvC,UAAU,EAAO;IACjB,qBAAqB,EAAO;IAC7B,CAAC,GAGG;;AAET,MAAI,EAAO,SAAS,mBAAmB;GACrC,IAAI,IAAoB,EAAO;AAC/B,UAAO,EAAa,eAAc,MAAO;AAKvC,IAHK,MAAM,QAAQ,EAAkB,KACnC,IAAoB,CAAC,EAAkB,GAEzC,EAAkB,SAAQ,MAAoB;AAC5C,KAAI,EAAW,GAAkB,EAAa,GAE5C,EAAI,OAAO,EAAiB,SAAS,IAGhC,KACH,EAAI,OAAO,EAEb,EAAI,IAAI,EAAiB,UAAU,EAAiB;MAEtD;KACF;;AAEJ,SAAO;IAET,CAAC,GAAY,EAAe,
|
|
1
|
+
{"version":3,"file":"useEntitySelection.js","names":[],"sources":["../../../src/components/EntityFinder/useEntitySelection.ts"],"sourcesContent":["import { Reference } from '@sage-bionetworks/synapse-types'\nimport { Map } from 'immutable'\nimport { isEqual } from 'lodash-es'\nimport { useCallback, useReducer } from 'react'\n\n// Represents the selection type. The key is the entity ID. Only one version of an entity can be chosen at a time.\nexport type EntitySelectionMapType = Map<string, Reference>\n\nexport function useEntitySelection(\n selectMultiple: boolean,\n initialSelection: EntitySelectionMapType = Map<string, Reference>(),\n) {\n /**\n *\n * @param entity The entity to check selected status\n * @param selected the list of selected entities\n * @returns a boolean array of length equal to entities.length denoting selection status\n */\n const isSelected = useCallback(\n (entity: Reference, selected: EntitySelectionMapType): boolean => {\n const match = selected.get(entity.targetId)\n if (match == null) {\n return false\n }\n return isEqual(entity, match)\n },\n [],\n )\n\n type ReducerAction =\n | {\n type: 'toggleSelection'\n toggledReferences: Reference | Reference[]\n }\n | {\n type: 'setSelection'\n selection: EntitySelectionMapType\n }\n | {\n type: 'setInitialVersion'\n entityId: string\n version: number\n }\n\n /**\n * Given the existing selections and a list of toggled references, return the new list of selections\n * @param currentState\n * @param action\n * @returns\n */\n const entitySelectionReducer = useCallback(\n (\n currentState: EntitySelectionMapType,\n action: ReducerAction,\n ): EntitySelectionMapType => {\n if (action.type === 'setSelection') {\n return action.selection\n }\n if (action.type === 'setInitialVersion') {\n // Safe to call from effects multiple times — only acts the first time\n const existing = currentState.get(action.entityId)\n if (existing != null && existing.targetVersionNumber == null) {\n return currentState.set(action.entityId, {\n targetId: action.entityId,\n targetVersionNumber: action.version,\n })\n }\n // Entity not selected, or already has a version - no-op\n return currentState\n }\n if (action.type === 'toggleSelection') {\n let toggledReferences = action.toggledReferences\n return currentState.withMutations(map => {\n // Note: we currently don't allow selecting two versions of the same entity, so we replace previous selected version with new selected version\n if (!Array.isArray(toggledReferences)) {\n toggledReferences = [toggledReferences]\n }\n toggledReferences.forEach(toggledReference => {\n if (isSelected(toggledReference, currentState)) {\n // remove from selection\n map.delete(toggledReference.targetId)\n } else {\n // add to selection\n if (!selectMultiple) {\n map.clear()\n }\n map.set(toggledReference.targetId, toggledReference)\n }\n })\n })\n }\n return currentState\n },\n [isSelected, selectMultiple],\n )\n\n const [selectedEntities, dispatch] = useReducer(\n entitySelectionReducer,\n initialSelection,\n )\n\n const toggleSelection = useCallback(\n (toggledReferences: Reference | Reference[]) =>\n dispatch({ type: 'toggleSelection', toggledReferences }),\n [],\n )\n\n const setSelection = useCallback(\n (selection: EntitySelectionMapType) =>\n dispatch({ type: 'setSelection', selection }),\n [],\n )\n\n const setInitialVersion = useCallback(\n (entityId: string, version: number) =>\n dispatch({ type: 'setInitialVersion', entityId, version }),\n [],\n )\n\n return {\n selectedEntities,\n toggleSelection,\n setSelection,\n setInitialVersion,\n }\n}\n"],"mappings":";;;;AAQA,SAAgB,EACd,GACA,IAA2C,GAAwB,EACnE;CAOA,IAAM,IAAa,GAChB,GAAmB,MAA8C;EAChE,IAAM,IAAQ,EAAS,IAAI,EAAO,SAAS;AAI3C,SAHI,KAAS,OACJ,KAEF,EAAQ,GAAQ,EAAM;IAE/B,EAAE,CACH,EAqEK,CAAC,GAAkB,KAAY,EA9CN,GAE3B,GACA,MAC2B;AAC3B,MAAI,EAAO,SAAS,eAClB,QAAO,EAAO;AAEhB,MAAI,EAAO,SAAS,qBAAqB;GAEvC,IAAM,IAAW,EAAa,IAAI,EAAO,SAAS;AAQlD,UAPI,KAAY,QAAQ,EAAS,uBAAuB,OAC/C,EAAa,IAAI,EAAO,UAAU;IACvC,UAAU,EAAO;IACjB,qBAAqB,EAAO;IAC7B,CAAC,GAGG;;AAET,MAAI,EAAO,SAAS,mBAAmB;GACrC,IAAI,IAAoB,EAAO;AAC/B,UAAO,EAAa,eAAc,MAAO;AAKvC,IAHK,MAAM,QAAQ,EAAkB,KACnC,IAAoB,CAAC,EAAkB,GAEzC,EAAkB,SAAQ,MAAoB;AAC5C,KAAI,EAAW,GAAkB,EAAa,GAE5C,EAAI,OAAO,EAAiB,SAAS,IAGhC,KACH,EAAI,OAAO,EAEb,EAAI,IAAI,EAAiB,UAAU,EAAiB;MAEtD;KACF;;AAEJ,SAAO;IAET,CAAC,GAAY,EAAe,CAI5B,EACA,EACD;AAoBD,QAAO;EACL;EACA,iBApBsB,GACrB,MACC,EAAS;GAAE,MAAM;GAAmB;GAAmB,CAAC,EAC1D,EAAE,CAiBF;EACA,cAfmB,GAClB,MACC,EAAS;GAAE,MAAM;GAAgB;GAAW,CAAC,EAC/C,EAAE,CAYF;EACA,mBAVwB,GACvB,GAAkB,MACjB,EAAS;GAAE,MAAM;GAAqB;GAAU;GAAS,CAAC,EAC5D,EAAE,CAOF;EACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityForm.js","names":[],"sources":["../../../src/components/EntityForm/EntityForm.tsx"],"sourcesContent":["// EntityForm:\nimport SynapseClient from '@/synapse-client'\nimport { getFileHandleContent } from '@/synapse-client/SynapseClient'\nimport { SynapseContext } from '@/utils/context/SynapseContext'\n// Will generate a Form (based on your schema files).\n// Gathers user input (including files)\n// Will give you the Synapse ID of the FileEntity that contains the user form data.\nimport Form from '@rjsf/core'\nimport validator from '@rjsf/validator-ajv8'\nimport {\n EntityId,\n EntityLookupRequest,\n FileEntity,\n UserProfile,\n} from '@sage-bionetworks/synapse-types'\nimport { Component, ContextType, createRef } from 'react'\n\nexport type EntityFormProps = {\n // Provide the parent container (folder/project), that should contain a folder (named <user_id>) that this user can write to.\n parentContainerId: string\n formSchemaEntityId: string // Synapse file that contains the form schema.\n formUiSchemaEntityId: string // Synapse file that contains the form ui schema.\n initFormData: boolean // If true, it indicates that you’d like to download and pre-fill the form with the user's previous response.\n synIdCallback: (synId: string) => void // callback. Once the form output has been saved to a FileEntity, will send synID back\n}\ntype EntityFormState = {\n error?: any\n isLoading?: boolean\n successfullyUploaded: boolean\n containerId?: string\n userprofile?: UserProfile\n currentFileEntity?: FileEntity // file holding user form data\n formData?: any // form data that prepopulates the form\n formSchema?: any // schema that drives the form\n formUiSchema?: any // ui schema that directs how to render the form elements\n}\n\nexport class EntityForm extends Component<EntityFormProps, EntityFormState> {\n static contextType = SynapseContext\n declare context: NonNullable<ContextType<typeof SynapseContext>>\n\n formRef: any\n\n constructor(props: EntityFormProps) {\n super(props)\n this.formRef = createRef()\n this.state = {\n isLoading: true,\n successfullyUploaded: false,\n }\n }\n\n componentDidMount() {\n this.refresh()\n }\n\n submitForm = () => {\n this.formRef.current.submit()\n }\n\n refresh = () => {\n if (this.context.isAuthenticated && this.context.accessToken) {\n const promises = [\n SynapseClient.getUserProfile(this.context.accessToken),\n SynapseClient.getEntity(\n this.context.accessToken,\n this.props.formSchemaEntityId,\n ),\n SynapseClient.getEntity(\n this.context.accessToken,\n this.props.formUiSchemaEntityId,\n ),\n ] as Promise<any>[]\n Promise.all(promises)\n .then(values => {\n const userprofile: UserProfile = values[0]\n this.getTargetContainer(userprofile, this.context.accessToken!).then(\n // @ts-ignore\n (targetContainerId: string) => {\n const formSchemaFileEntity: FileEntity = values[1]\n const formUiSchemaFileEntity: FileEntity = values[2]\n this.getSchemaFileContent(\n targetContainerId,\n formSchemaFileEntity,\n formUiSchemaFileEntity,\n )\n },\n )\n })\n .catch(error => {\n this.onError(error)\n })\n }\n }\n\n getSchemaFileContent = (\n targetFolderId: string,\n formSchemaFileEntity: FileEntity,\n formUiSchemaFileEntity: FileEntity,\n ) => {\n const promises = [\n SynapseClient.getFileResult(\n formSchemaFileEntity,\n this.context.accessToken,\n true,\n true,\n ),\n SynapseClient.getFileResult(\n formUiSchemaFileEntity,\n this.context.accessToken,\n true,\n true,\n ),\n ]\n\n Promise.all(promises)\n .then(values => {\n try {\n const fileContent = values.map(el => {\n return getFileHandleContent(el.fileHandle!, el.preSignedURL!)\n })\n Promise.all(fileContent)\n .then(el => {\n const formSchemaContent = JSON.parse(el[0])\n const formUiSchemaContent = JSON.parse(el[1])\n this.getExistingFileData(\n targetFolderId,\n formSchemaContent,\n formUiSchemaContent,\n )\n })\n .catch(e => {\n console.log('getSchemaFileContent: Error getting form content', e)\n })\n } catch (e) {\n console.log('getSchemaFileContent: Error getting schema content', e)\n }\n })\n .catch(error => {\n this.onError(error)\n })\n }\n\n getExistingFileData = (\n targetFolderId: string,\n formSchemaContent: any,\n formUiSchemaContent: any,\n ) => {\n // if data already exists, save a reference to the existing entity and prefill the form\n const fileName = `${formSchemaContent.title}.json`\n const entityLookupRequest = {\n entityName: fileName,\n parentId: targetFolderId,\n }\n let formData: any\n let currentFileEntity: FileEntity\n SynapseClient.lookupChildEntity(\n entityLookupRequest,\n this.context.accessToken,\n )\n .then((entityId: EntityId) => {\n // ok, found the existing file\n return SynapseClient.getEntity<FileEntity>(\n this.context.accessToken,\n entityId.id,\n ).then(entity => {\n currentFileEntity = entity\n if (this.props.initFormData) {\n return SynapseClient.getFileResult(\n currentFileEntity,\n this.context.accessToken,\n true,\n true,\n ).then(async existingFileData => {\n try {\n const fileContent = await getFileHandleContent(\n existingFileData.fileHandle!,\n existingFileData.preSignedURL!,\n )\n formData = JSON.parse(fileContent)\n } catch (e) {\n console.log('getExistingFileData: Error setting form data', e)\n }\n })\n }\n // else we're done\n return Promise.resolve()\n })\n })\n .finally(() => {\n this.setState({\n formData,\n currentFileEntity,\n formSchema: formSchemaContent,\n formUiSchema: formUiSchemaContent,\n })\n })\n }\n\n getTargetContainer = async (userprofile: UserProfile, token: string) => {\n const entityLookupRequest: EntityLookupRequest = {\n entityName: userprofile.ownerId,\n parentId: this.props.parentContainerId,\n }\n try {\n const entityId = await SynapseClient.lookupChildEntity(\n entityLookupRequest,\n token,\n )\n // ok, found an entity of the same name.\n console.log(\n `EntityForm uploading to https://www.synapse.org/Synapse:${entityId.id}`,\n )\n this.setState({\n userprofile,\n containerId: entityId.id,\n isLoading: false,\n })\n return entityId.id\n } catch (error) {\n if (error.status === 404) {\n return this.onError(\n new Error(\n 'Your folder has not yet been set up, please retry in a few minutes.',\n ),\n )\n }\n // else\n return this.onError(error)\n }\n }\n\n finishedProcessing = () => {\n this.setState({\n isLoading: false,\n successfullyUploaded: true,\n })\n }\n\n onError = (error: any) => {\n this.setState({\n error,\n isLoading: false,\n successfullyUploaded: false,\n })\n }\n\n onSubmit = ({ formData }: any) => {\n this.setState({\n isLoading: true,\n successfullyUploaded: false,\n })\n\n const submissionFileAndForm: Blob = new Blob([JSON.stringify(formData)], {\n type: 'text/json',\n })\n this.createEntityFile(submissionFileAndForm)\n }\n\n createEntityFile = (fileContentsBlob: Blob) => {\n const fileName = `${this.state.formSchema.title}.json`\n SynapseClient.uploadFile(\n this.context.accessToken,\n fileName,\n fileContentsBlob,\n )\n .then(fileUploadComplete => {\n // do we need to create a new file entity, or update an existing file entity?\n const newFileHandleId = fileUploadComplete.fileHandleId\n if (this.state.currentFileEntity) {\n const updatedFileEntity = {\n ...this.state.currentFileEntity,\n dataFileHandleId: newFileHandleId,\n }\n this.setState({\n currentFileEntity: updatedFileEntity,\n })\n return SynapseClient.updateEntity(\n updatedFileEntity,\n this.context.accessToken,\n )\n }\n // else, it's a new file entity\n const newFileEntity: FileEntity = {\n parentId: this.state.containerId!,\n name: fileName,\n concreteType: 'org.sagebionetworks.repo.model.FileEntity',\n dataFileHandleId: newFileHandleId,\n }\n return SynapseClient.createEntity(\n newFileEntity,\n this.context.accessToken,\n )\n })\n .then(fileEntity => {\n // by this point we've either found and updated the existing file entity, or created a new one.\n this.finishedProcessing()\n if (this.props.synIdCallback) {\n this.props.synIdCallback(fileEntity.id!)\n }\n })\n .catch((error: any) => {\n this.onError(error)\n })\n }\n\n render() {\n return (\n <div>\n {this.state.error && (\n <div className=\"panel panel-danger errors\">\n <div className=\"panel-heading\">\n <h3 className=\"panel-title\">Error</h3>\n </div>\n <ul className=\"list-group\">\n <li className=\"list-group-item text-danger\">\n {this.state.error.name} {this.state.error.reason}\n {this.state.error.message}\n </li>\n </ul>\n </div>\n )}\n {this.context.accessToken &&\n !this.state.isLoading &&\n !this.state.successfullyUploaded &&\n this.state.formSchema &&\n this.state.formUiSchema &&\n !this.state.error && (\n <Form\n validator={validator}\n formData={this.state.formData}\n schema={this.state.formSchema}\n uiSchema={this.state.formUiSchema}\n onSubmit={this.onSubmit}\n ref={this.formRef}\n >\n <div style={{ display: 'none' }}>\n <button type=\"submit\" className=\"btn btn-info\">\n Submit\n </button>\n </div>\n </Form>\n )}\n {!this.state.error &&\n this.context.accessToken &&\n this.state.isLoading && (\n <>\n <span>Saving…</span>\n <span style={{ marginLeft: '2px' }} className={'spinner'} />\n </>\n )}\n </div>\n )\n }\n}\n\nexport default EntityForm\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EntityForm.js","names":[],"sources":["../../../src/components/EntityForm/EntityForm.tsx"],"sourcesContent":["// EntityForm:\nimport SynapseClient from '@/synapse-client'\nimport { getFileHandleContent } from '@/synapse-client/SynapseClient'\nimport { SynapseContext } from '@/utils/context/SynapseContext'\n// Will generate a Form (based on your schema files).\n// Gathers user input (including files)\n// Will give you the Synapse ID of the FileEntity that contains the user form data.\nimport Form from '@rjsf/core'\nimport validator from '@rjsf/validator-ajv8'\nimport {\n EntityId,\n EntityLookupRequest,\n FileEntity,\n UserProfile,\n} from '@sage-bionetworks/synapse-types'\nimport { Component, ContextType, createRef } from 'react'\n\nexport type EntityFormProps = {\n // Provide the parent container (folder/project), that should contain a folder (named <user_id>) that this user can write to.\n parentContainerId: string\n formSchemaEntityId: string // Synapse file that contains the form schema.\n formUiSchemaEntityId: string // Synapse file that contains the form ui schema.\n initFormData: boolean // If true, it indicates that you’d like to download and pre-fill the form with the user's previous response.\n synIdCallback: (synId: string) => void // callback. Once the form output has been saved to a FileEntity, will send synID back\n}\ntype EntityFormState = {\n error?: any\n isLoading?: boolean\n successfullyUploaded: boolean\n containerId?: string\n userprofile?: UserProfile\n currentFileEntity?: FileEntity // file holding user form data\n formData?: any // form data that prepopulates the form\n formSchema?: any // schema that drives the form\n formUiSchema?: any // ui schema that directs how to render the form elements\n}\n\nexport class EntityForm extends Component<EntityFormProps, EntityFormState> {\n static contextType = SynapseContext\n declare context: NonNullable<ContextType<typeof SynapseContext>>\n\n formRef: any\n\n constructor(props: EntityFormProps) {\n super(props)\n this.formRef = createRef()\n this.state = {\n isLoading: true,\n successfullyUploaded: false,\n }\n }\n\n componentDidMount() {\n this.refresh()\n }\n\n submitForm = () => {\n this.formRef.current.submit()\n }\n\n refresh = () => {\n if (this.context.isAuthenticated && this.context.accessToken) {\n const promises = [\n SynapseClient.getUserProfile(this.context.accessToken),\n SynapseClient.getEntity(\n this.context.accessToken,\n this.props.formSchemaEntityId,\n ),\n SynapseClient.getEntity(\n this.context.accessToken,\n this.props.formUiSchemaEntityId,\n ),\n ] as Promise<any>[]\n Promise.all(promises)\n .then(values => {\n const userprofile: UserProfile = values[0]\n this.getTargetContainer(userprofile, this.context.accessToken!).then(\n // @ts-ignore\n (targetContainerId: string) => {\n const formSchemaFileEntity: FileEntity = values[1]\n const formUiSchemaFileEntity: FileEntity = values[2]\n this.getSchemaFileContent(\n targetContainerId,\n formSchemaFileEntity,\n formUiSchemaFileEntity,\n )\n },\n )\n })\n .catch(error => {\n this.onError(error)\n })\n }\n }\n\n getSchemaFileContent = (\n targetFolderId: string,\n formSchemaFileEntity: FileEntity,\n formUiSchemaFileEntity: FileEntity,\n ) => {\n const promises = [\n SynapseClient.getFileResult(\n formSchemaFileEntity,\n this.context.accessToken,\n true,\n true,\n ),\n SynapseClient.getFileResult(\n formUiSchemaFileEntity,\n this.context.accessToken,\n true,\n true,\n ),\n ]\n\n Promise.all(promises)\n .then(values => {\n try {\n const fileContent = values.map(el => {\n return getFileHandleContent(el.fileHandle!, el.preSignedURL!)\n })\n Promise.all(fileContent)\n .then(el => {\n const formSchemaContent = JSON.parse(el[0])\n const formUiSchemaContent = JSON.parse(el[1])\n this.getExistingFileData(\n targetFolderId,\n formSchemaContent,\n formUiSchemaContent,\n )\n })\n .catch(e => {\n console.log('getSchemaFileContent: Error getting form content', e)\n })\n } catch (e) {\n console.log('getSchemaFileContent: Error getting schema content', e)\n }\n })\n .catch(error => {\n this.onError(error)\n })\n }\n\n getExistingFileData = (\n targetFolderId: string,\n formSchemaContent: any,\n formUiSchemaContent: any,\n ) => {\n // if data already exists, save a reference to the existing entity and prefill the form\n const fileName = `${formSchemaContent.title}.json`\n const entityLookupRequest = {\n entityName: fileName,\n parentId: targetFolderId,\n }\n let formData: any\n let currentFileEntity: FileEntity\n SynapseClient.lookupChildEntity(\n entityLookupRequest,\n this.context.accessToken,\n )\n .then((entityId: EntityId) => {\n // ok, found the existing file\n return SynapseClient.getEntity<FileEntity>(\n this.context.accessToken,\n entityId.id,\n ).then(entity => {\n currentFileEntity = entity\n if (this.props.initFormData) {\n return SynapseClient.getFileResult(\n currentFileEntity,\n this.context.accessToken,\n true,\n true,\n ).then(async existingFileData => {\n try {\n const fileContent = await getFileHandleContent(\n existingFileData.fileHandle!,\n existingFileData.preSignedURL!,\n )\n formData = JSON.parse(fileContent)\n } catch (e) {\n console.log('getExistingFileData: Error setting form data', e)\n }\n })\n }\n // else we're done\n return Promise.resolve()\n })\n })\n .finally(() => {\n this.setState({\n formData,\n currentFileEntity,\n formSchema: formSchemaContent,\n formUiSchema: formUiSchemaContent,\n })\n })\n }\n\n getTargetContainer = async (userprofile: UserProfile, token: string) => {\n const entityLookupRequest: EntityLookupRequest = {\n entityName: userprofile.ownerId,\n parentId: this.props.parentContainerId,\n }\n try {\n const entityId = await SynapseClient.lookupChildEntity(\n entityLookupRequest,\n token,\n )\n // ok, found an entity of the same name.\n console.log(\n `EntityForm uploading to https://www.synapse.org/Synapse:${entityId.id}`,\n )\n this.setState({\n userprofile,\n containerId: entityId.id,\n isLoading: false,\n })\n return entityId.id\n } catch (error) {\n if (error.status === 404) {\n return this.onError(\n new Error(\n 'Your folder has not yet been set up, please retry in a few minutes.',\n ),\n )\n }\n // else\n return this.onError(error)\n }\n }\n\n finishedProcessing = () => {\n this.setState({\n isLoading: false,\n successfullyUploaded: true,\n })\n }\n\n onError = (error: any) => {\n this.setState({\n error,\n isLoading: false,\n successfullyUploaded: false,\n })\n }\n\n onSubmit = ({ formData }: any) => {\n this.setState({\n isLoading: true,\n successfullyUploaded: false,\n })\n\n const submissionFileAndForm: Blob = new Blob([JSON.stringify(formData)], {\n type: 'text/json',\n })\n this.createEntityFile(submissionFileAndForm)\n }\n\n createEntityFile = (fileContentsBlob: Blob) => {\n const fileName = `${this.state.formSchema.title}.json`\n SynapseClient.uploadFile(\n this.context.accessToken,\n fileName,\n fileContentsBlob,\n )\n .then(fileUploadComplete => {\n // do we need to create a new file entity, or update an existing file entity?\n const newFileHandleId = fileUploadComplete.fileHandleId\n if (this.state.currentFileEntity) {\n const updatedFileEntity = {\n ...this.state.currentFileEntity,\n dataFileHandleId: newFileHandleId,\n }\n this.setState({\n currentFileEntity: updatedFileEntity,\n })\n return SynapseClient.updateEntity(\n updatedFileEntity,\n this.context.accessToken,\n )\n }\n // else, it's a new file entity\n const newFileEntity: FileEntity = {\n parentId: this.state.containerId!,\n name: fileName,\n concreteType: 'org.sagebionetworks.repo.model.FileEntity',\n dataFileHandleId: newFileHandleId,\n }\n return SynapseClient.createEntity(\n newFileEntity,\n this.context.accessToken,\n )\n })\n .then(fileEntity => {\n // by this point we've either found and updated the existing file entity, or created a new one.\n this.finishedProcessing()\n if (this.props.synIdCallback) {\n this.props.synIdCallback(fileEntity.id!)\n }\n })\n .catch((error: any) => {\n this.onError(error)\n })\n }\n\n render() {\n return (\n <div>\n {this.state.error && (\n <div className=\"panel panel-danger errors\">\n <div className=\"panel-heading\">\n <h3 className=\"panel-title\">Error</h3>\n </div>\n <ul className=\"list-group\">\n <li className=\"list-group-item text-danger\">\n {this.state.error.name} {this.state.error.reason}\n {this.state.error.message}\n </li>\n </ul>\n </div>\n )}\n {this.context.accessToken &&\n !this.state.isLoading &&\n !this.state.successfullyUploaded &&\n this.state.formSchema &&\n this.state.formUiSchema &&\n !this.state.error && (\n <Form\n validator={validator}\n formData={this.state.formData}\n schema={this.state.formSchema}\n uiSchema={this.state.formUiSchema}\n onSubmit={this.onSubmit}\n ref={this.formRef}\n >\n <div style={{ display: 'none' }}>\n <button type=\"submit\" className=\"btn btn-info\">\n Submit\n </button>\n </div>\n </Form>\n )}\n {!this.state.error &&\n this.context.accessToken &&\n this.state.isLoading && (\n <>\n <span>Saving…</span>\n <span style={{ marginLeft: '2px' }} className={'spinner'} />\n </>\n )}\n </div>\n )\n }\n}\n\nexport default EntityForm\n"],"mappings":";;;;;;;;AAqCA,IAAa,IAAb,cAAgC,EAA4C;CAC1E,OAAO,cAAc;CAGrB;CAEA,YAAY,GAAwB;AAGlC,EAFA,MAAM,EAAM,EACZ,KAAK,UAAU,GAAW,EAC1B,KAAK,QAAQ;GACX,WAAW;GACX,sBAAsB;GACvB;;CAGH,oBAAoB;AAClB,OAAK,SAAS;;CAGhB,mBAAmB;AACjB,OAAK,QAAQ,QAAQ,QAAQ;;CAG/B,gBAAgB;AACd,MAAI,KAAK,QAAQ,mBAAmB,KAAK,QAAQ,aAAa;GAC5D,IAAM,IAAW;IACf,EAAc,eAAe,KAAK,QAAQ,YAAY;IACtD,EAAc,UACZ,KAAK,QAAQ,aACb,KAAK,MAAM,mBACZ;IACD,EAAc,UACZ,KAAK,QAAQ,aACb,KAAK,MAAM,qBACZ;IACF;AACD,WAAQ,IAAI,EAAS,CAClB,MAAK,MAAU;IACd,IAAM,IAA2B,EAAO;AACxC,SAAK,mBAAmB,GAAa,KAAK,QAAQ,YAAa,CAAC,MAE7D,MAA8B;KAC7B,IAAM,IAAmC,EAAO,IAC1C,IAAqC,EAAO;AAClD,UAAK,qBACH,GACA,GACA,EACD;MAEJ;KACD,CACD,OAAM,MAAS;AACd,SAAK,QAAQ,EAAM;KACnB;;;CAIR,wBACE,GACA,GACA,MACG;EACH,IAAM,IAAW,CACf,EAAc,cACZ,GACA,KAAK,QAAQ,aACb,IACA,GACD,EACD,EAAc,cACZ,GACA,KAAK,QAAQ,aACb,IACA,GACD,CACF;AAED,UAAQ,IAAI,EAAS,CAClB,MAAK,MAAU;AACd,OAAI;IACF,IAAM,IAAc,EAAO,KAAI,MACtB,EAAqB,EAAG,YAAa,EAAG,aAAc,CAC7D;AACF,YAAQ,IAAI,EAAY,CACrB,MAAK,MAAM;KACV,IAAM,IAAoB,KAAK,MAAM,EAAG,GAAG,EACrC,IAAsB,KAAK,MAAM,EAAG,GAAG;AAC7C,UAAK,oBACH,GACA,GACA,EACD;MACD,CACD,OAAM,MAAK;AACV,aAAQ,IAAI,oDAAoD,EAAE;MAClE;YACG,GAAG;AACV,YAAQ,IAAI,sDAAsD,EAAE;;IAEtE,CACD,OAAM,MAAS;AACd,QAAK,QAAQ,EAAM;IACnB;;CAGN,uBACE,GACA,GACA,MACG;EAGH,IAAM,IAAsB;GAC1B,YAAY,GAFM,EAAkB,MAAM;GAG1C,UAAU;GACX,EACG,GACA;AACJ,IAAc,kBACZ,GACA,KAAK,QAAQ,YACd,CACE,MAAM,MAEE,EAAc,UACnB,KAAK,QAAQ,aACb,EAAS,GACV,CAAC,MAAK,OACL,IAAoB,GAChB,KAAK,MAAM,eACN,EAAc,cACnB,GACA,KAAK,QAAQ,aACb,IACA,GACD,CAAC,KAAK,OAAM,MAAoB;AAC/B,OAAI;IACF,IAAM,IAAc,MAAM,EACxB,EAAiB,YACjB,EAAiB,aAClB;AACD,QAAW,KAAK,MAAM,EAAY;YAC3B,GAAG;AACV,YAAQ,IAAI,gDAAgD,EAAE;;IAEhE,GAGG,QAAQ,SAAS,EACxB,CACF,CACD,cAAc;AACb,QAAK,SAAS;IACZ;IACA;IACA,YAAY;IACZ,cAAc;IACf,CAAC;IACF;;CAGN,qBAAqB,OAAO,GAA0B,MAAkB;EACtE,IAAM,IAA2C;GAC/C,YAAY,EAAY;GACxB,UAAU,KAAK,MAAM;GACtB;AACD,MAAI;GACF,IAAM,IAAW,MAAM,EAAc,kBACnC,GACA,EACD;AAUD,UARA,QAAQ,IACN,2DAA2D,EAAS,KACrE,EACD,KAAK,SAAS;IACZ;IACA,aAAa,EAAS;IACtB,WAAW;IACZ,CAAC,EACK,EAAS;WACT,GAAO;AASd,UARI,EAAM,WAAW,MACZ,KAAK,QACV,gBAAI,MACF,sEACD,CACF,GAGI,KAAK,QAAQ,EAAM;;;CAI9B,2BAA2B;AACzB,OAAK,SAAS;GACZ,WAAW;GACX,sBAAsB;GACvB,CAAC;;CAGJ,WAAW,MAAe;AACxB,OAAK,SAAS;GACZ;GACA,WAAW;GACX,sBAAsB;GACvB,CAAC;;CAGJ,YAAY,EAAE,kBAAoB;AAChC,OAAK,SAAS;GACZ,WAAW;GACX,sBAAsB;GACvB,CAAC;EAEF,IAAM,IAA8B,IAAI,KAAK,CAAC,KAAK,UAAU,EAAS,CAAC,EAAE,EACvE,MAAM,aACP,CAAC;AACF,OAAK,iBAAiB,EAAsB;;CAG9C,oBAAoB,MAA2B;EAC7C,IAAM,IAAW,GAAG,KAAK,MAAM,WAAW,MAAM;AAChD,IAAc,WACZ,KAAK,QAAQ,aACb,GACA,EACD,CACE,MAAK,MAAsB;GAE1B,IAAM,IAAkB,EAAmB;AAC3C,OAAI,KAAK,MAAM,mBAAmB;IAChC,IAAM,IAAoB;KACxB,GAAG,KAAK,MAAM;KACd,kBAAkB;KACnB;AAID,WAHA,KAAK,SAAS,EACZ,mBAAmB,GACpB,CAAC,EACK,EAAc,aACnB,GACA,KAAK,QAAQ,YACd;;GAGH,IAAM,IAA4B;IAChC,UAAU,KAAK,MAAM;IACrB,MAAM;IACN,cAAc;IACd,kBAAkB;IACnB;AACD,UAAO,EAAc,aACnB,GACA,KAAK,QAAQ,YACd;IACD,CACD,MAAK,MAAc;AAGlB,GADA,KAAK,oBAAoB,EACrB,KAAK,MAAM,iBACb,KAAK,MAAM,cAAc,EAAW,GAAI;IAE1C,CACD,OAAO,MAAe;AACrB,QAAK,QAAQ,EAAM;IACnB;;CAGN,SAAS;AACP,SACE,kBAAC,OAAD,EAAA,UAAA;GACG,KAAK,MAAM,SACV,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,MAAD;MAAI,WAAU;gBAAc;MAAU,CAAA;KAClC,CAAA,EACN,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACG,KAAK,MAAM,MAAM;OAAK;OAAE,KAAK,MAAM,MAAM;OACzC,KAAK,MAAM,MAAM;OACf;;KACF,CAAA,CACD;;GAEP,KAAK,QAAQ,eACZ,CAAC,KAAK,MAAM,aACZ,CAAC,KAAK,MAAM,wBACZ,KAAK,MAAM,cACX,KAAK,MAAM,gBACX,CAAC,KAAK,MAAM,SACV,kBAAC,GAAD;IACa;IACX,UAAU,KAAK,MAAM;IACrB,QAAQ,KAAK,MAAM;IACnB,UAAU,KAAK,MAAM;IACrB,UAAU,KAAK;IACf,KAAK,KAAK;cAEV,kBAAC,OAAD;KAAK,OAAO,EAAE,SAAS,QAAQ;eAC7B,kBAAC,UAAD;MAAQ,MAAK;MAAS,WAAU;gBAAe;MAEtC,CAAA;KACL,CAAA;IACD,CAAA;GAEV,CAAC,KAAK,MAAM,SACX,KAAK,QAAQ,eACb,KAAK,MAAM,aACT,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,QAAD,EAAA,UAAM,WAAqB,CAAA,EAC3B,kBAAC,QAAD;IAAM,OAAO,EAAE,YAAY,OAAO;IAAE,WAAW;IAAa,CAAA,CAC3D,EAAA,CAAA;GAEH,EAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityHeaderTable.js","names":[],"sources":["../../../src/components/EntityHeaderTable/EntityHeaderTable.tsx"],"sourcesContent":["import React from 'react'\nimport AddAd from '@/assets/icons/AddAd'\nimport { StyledTableContainer } from '@/components/styled/StyledTableContainer'\nimport { useGetEntityHeaders } from '@/synapse-queries/entity/useGetEntityHeaders'\nimport {\n entityTypeToFriendlyName,\n getEntityTypeFromHeader,\n normalizeSynPrefix,\n} from '@/utils/functions/EntityTypeUtils'\nimport { SYNAPSE_ENTITY_ID_REGEX } from '@/utils/functions/RegularExpressions'\nimport { AddCircleTwoTone } from '@mui/icons-material'\nimport {\n Alert,\n AlertTitle,\n Box,\n Button,\n IconButton,\n InputLabel,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport { EntityHeader, ReferenceList } from '@sage-bionetworks/synapse-types'\nimport {\n ColumnDef,\n ColumnFiltersState,\n flexRender,\n getCoreRowModel,\n getFacetedMinMaxValues,\n getFacetedRowModel,\n getFacetedUniqueValues,\n getFilteredRowModel,\n getSortedRowModel,\n useReactTable,\n} from '@tanstack/react-table'\nimport { noop, upperFirst } from 'lodash-es'\nimport papaparseLib from 'papaparse'\n// papaparse is CJS-only; pull named values from the default export.\nconst { parse } = papaparseLib\nimport pluralize from 'pluralize'\nimport { Fragment, useCallback, useEffect, useMemo, useState } from 'react'\nimport {\n EntityFinderModal,\n EntityFinderModalProps,\n} from '../EntityFinder/EntityFinderModal'\nimport { FinderScope } from '../EntityFinder/tree/EntityTree'\nimport { VersionSelectionType } from '../EntityFinder/VersionSelectionType'\nimport IconSvg from '../IconSvg'\nimport { SkeletonTable } from '../Skeleton'\nimport {\n CheckBoxCell,\n CheckBoxHeader,\n EntityHeaderIDCell,\n EntityHeaderNameCell,\n EntityHeaderTypeCell,\n} from './EntityHeaderTableCellRenderers'\nimport { Filter } from './Filter'\nimport { useEntityHeaderTableState } from './useEntityHeaderTableState'\n\nconst DEFAULT_FINDER_CONFIG: EntityFinderModalProps['configuration'] = {\n selectMultiple: true,\n versionSelection: VersionSelectionType.DISALLOWED,\n initialScope: FinderScope.ALL_PROJECTS,\n initialContainer: 'root',\n}\n\nexport type EntityHeaderTableProps = {\n references: ReferenceList\n isEditable: boolean\n disabled?: boolean\n onUpdate?: (updatedRefs: ReferenceList) => void // when the references are updated, EntityHeaderTable will call this function with the updated list\n removeSelectedRowsButtonText?: string\n onUpdateEntityIDsTextbox?: (value: string) => void // when the entity IDs text box is updated, this is called\n /* The word used to describe the items in the table. Default 'entity' */\n objectNameCopy?: string\n // If true, the text field where IDs are pasted is hidden, and confirming the entity finder will immediately call `onUpdate`\n hideTextFieldToPasteValue?: boolean\n entityFinderConfiguration?: EntityFinderModalProps['configuration']\n}\n\nconst UNMANAGEABLE_SUBJECT_COUNT = 10\n\n// extend EntityHeader to create dummy EntityHeader rows for those that the current user cannot view\nexport type EntityHeaderOrDummy = EntityHeader & { isDummy?: boolean }\n\n/**\n * Renders a sortable/filterable table for a set of entity references. If editable, onUpdate will be called back\n * on any entity added/removed.\n */\nexport const EntityHeaderTable = (\n props: EntityHeaderTableProps,\n): React.ReactNode => {\n const {\n references,\n isEditable,\n disabled,\n onUpdate = noop,\n removeSelectedRowsButtonText = 'Remove Selected Rows',\n onUpdateEntityIDsTextbox,\n objectNameCopy = 'entity',\n hideTextFieldToPasteValue = false,\n entityFinderConfiguration = DEFAULT_FINDER_CONFIG,\n } = props\n const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])\n const [showEntityFinder, setShowEntityFinder] = useState<boolean>(false)\n\n const {\n rowSelection,\n setRowSelection,\n refsInState,\n setRefsInState,\n newEntityIDs,\n setNewEntityIDs,\n parseErrors,\n setParseErrors,\n } = useEntityHeaderTableState(references, onUpdateEntityIDsTextbox, onUpdate)\n\n const setInvalidEntityIDError = useCallback(\n (invalidEntityIDs: string[]) => {\n setParseErrors([`Invalid Synapse ID(s): ${invalidEntityIDs.join(',')}`])\n },\n [setParseErrors],\n )\n\n const addRefsFromEntityIDs = useCallback(\n (entityIDs: string[]) => {\n const newReferences: ReferenceList = entityIDs.map(id => {\n return {\n targetId: id.trim(),\n }\n })\n setRefsInState([...refsInState, ...newReferences])\n },\n [refsInState, setRefsInState],\n )\n\n const addPastedValuesToArray = useCallback(() => {\n if (newEntityIDs) {\n if (newEntityIDs.includes(',')) {\n parse<string[]>(newEntityIDs, {\n complete: result => {\n if (result.errors.length > 0) {\n const newParseErrors = result.errors.map(\n parseError => parseError.message,\n )\n setParseErrors(newParseErrors)\n } else {\n const newParsedEntityIDs = result.data[0]\n const invalidEntityIDs = newParsedEntityIDs.filter(\n id => !id.trim().match(SYNAPSE_ENTITY_ID_REGEX),\n )\n if (invalidEntityIDs.length > 0) {\n setInvalidEntityIDError(invalidEntityIDs)\n } else {\n addRefsFromEntityIDs(newParsedEntityIDs)\n }\n }\n },\n })\n } else {\n // single item\n if (!newEntityIDs.trim().match(SYNAPSE_ENTITY_ID_REGEX)) {\n setInvalidEntityIDError([newEntityIDs])\n } else {\n addRefsFromEntityIDs([newEntityIDs])\n }\n }\n } else {\n setParseErrors([])\n setNewEntityIDs('')\n }\n }, [\n addRefsFromEntityIDs,\n newEntityIDs,\n setInvalidEntityIDError,\n setNewEntityIDs,\n setParseErrors,\n ])\n\n const pluralObjectName = upperFirst(pluralize(objectNameCopy))\n\n const selectColumns: ColumnDef<EntityHeaderOrDummy, any>[] = useMemo(\n () => [\n {\n id: 'select',\n header: CheckBoxHeader,\n cell: CheckBoxCell,\n },\n ],\n [],\n )\n\n const entityHeaderColumns: ColumnDef<EntityHeaderOrDummy, any>[] = useMemo(\n () => [\n {\n accessorFn: (row: EntityHeaderOrDummy) => row.name,\n id: 'name',\n cell: EntityHeaderNameCell,\n header: 'Name',\n },\n {\n accessorFn: (row: EntityHeaderOrDummy) => row.id,\n id: 'id',\n cell: EntityHeaderIDCell,\n header: 'SynID',\n },\n {\n accessorFn: (row: EntityHeaderOrDummy) =>\n row.isDummy\n ? '-'\n : entityTypeToFriendlyName(getEntityTypeFromHeader(row)),\n id: 'type',\n header: 'Type',\n cell: EntityHeaderTypeCell,\n filterFn: 'includesString',\n },\n ],\n [],\n )\n\n const columns = useMemo<ColumnDef<EntityHeaderOrDummy, any>[]>(\n () =>\n isEditable\n ? selectColumns.concat(entityHeaderColumns)\n : entityHeaderColumns,\n [entityHeaderColumns, isEditable, selectColumns],\n )\n const selectionCount = Object.keys(rowSelection).length\n const {\n data: results,\n isSuccess,\n isLoading,\n } = useGetEntityHeaders(refsInState, {\n throwOnError: true,\n })\n\n const data = useMemo(() => {\n //create dummy entries for values that were not returned by the getEntityHeaders call!\n const newData = results ? results?.results : []\n const newDataEntityIds = new Set()\n newData.forEach(entityHeader =>\n newDataEntityIds.add(normalizeSynPrefix(entityHeader.id)),\n )\n const missingRefs = refsInState.filter(\n ref => !newDataEntityIds.has(normalizeSynPrefix(ref.targetId)),\n )\n const dummyEntityHeaders: EntityHeaderOrDummy[] = missingRefs.map(ref => {\n return {\n id: ref.targetId,\n name: ref.targetId,\n benefactorId: -1,\n type: 'org.sagebionetworks.repo.model.Project',\n createdOn: '',\n modifiedOn: '',\n createdBy: '',\n modifiedBy: '',\n isLatestVersion: true,\n isDummy: true,\n }\n })\n return newData.concat(dummyEntityHeaders)\n }, [refsInState, results])\n const table = useReactTable({\n data,\n columns,\n state: {\n rowSelection,\n columnFilters,\n },\n enableRowSelection: isEditable,\n onRowSelectionChange: setRowSelection,\n onColumnFiltersChange: setColumnFilters,\n getCoreRowModel: getCoreRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n getSortedRowModel: getSortedRowModel(),\n getFacetedRowModel: getFacetedRowModel(),\n getFacetedUniqueValues: getFacetedUniqueValues(),\n getFacetedMinMaxValues: getFacetedMinMaxValues(),\n // debugTable: true,\n // debugHeaders: true,\n // debugColumns: false,\n columnResizeMode: 'onChange',\n })\n\n const onRemove = useCallback(() => {\n // rowSelection looks like {3: true. 5: true} where the key is the row index.\n // Create a new ReferenceList based on the entityHeaders in the current table.\n const updatedData = data.filter(\n (_value, index) => !(rowSelection[index] === true),\n )\n const newRowRefs: ReferenceList = updatedData.map(entityHeader => {\n return {\n targetId: entityHeader.id,\n }\n })\n setRefsInState(newRowRefs)\n }, [data, rowSelection, setRefsInState])\n\n const isSelection = selectionCount > 0\n const totalRowCount = data.length\n const filteredRowCount = table.getPrePaginationRowModel().rows.length\n const showFilterControls = totalRowCount > UNMANAGEABLE_SUBJECT_COUNT\n\n /**\n * Reset the column filters when the filter controls are hidden.\n * This handles the following edge case:\n * 1. List contains 100 items of type \"A\" and 1 of type \"B\"\n * 2. User filters to show just \"A\" items\n * 3. User removes all \"A\" items\n * 4. Only the single \"B\" item remains, but the filter is still present on type \"A\".\n * The filter controls are hidden, so the user cannot see the \"B\" item.\n *\n * This effect will clear the filters when the filter controls are hidden, preventing this scenario.\n */\n useEffect(\n function resetFiltersWhenFilterControlsAreHidden() {\n if (!showFilterControls) {\n table.setColumnFilters([])\n }\n },\n [table, showFilterControls],\n )\n\n if (isLoading) {\n return (\n <SkeletonTable numCols={3} numRows={Math.min(10, refsInState.length)} />\n )\n } else if (!isSuccess) {\n return <></>\n }\n return (\n <div>\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'space-between',\n p: '12px 10px 10px 5px',\n }}\n >\n {showFilterControls && (\n <Typography variant=\"body1\" sx={{ marginBottom: '10px' }}>\n {totalRowCount} {pluralObjectName}{' '}\n {filteredRowCount < totalRowCount\n ? `(${filteredRowCount} visible)`\n : ''}\n {isSelection && <span>{` (${selectionCount} selected)`}</span>}\n </Typography>\n )}\n {isEditable && refsInState.length > 0 && (\n <Button\n variant=\"contained\"\n disabled={!isSelection || disabled}\n onClick={onRemove}\n >\n {removeSelectedRowsButtonText}\n </Button>\n )}\n </Box>\n <Box\n sx={{\n display: 'flex',\n pb: 2,\n }}\n >\n {table.getHeaderGroups().map(headerGroup =>\n headerGroup.headers.map(header => {\n return header.isPlaceholder ? null : (\n <Fragment key={header.column.id}>\n {header.column.getCanFilter() && showFilterControls ? (\n <Box sx={{ flexGrow: 1 }}>\n <Filter column={header.column} table={table} />\n </Box>\n ) : null}\n </Fragment>\n )\n }),\n )}\n </Box>\n {totalRowCount > 0 && (\n <StyledTableContainer\n sx={{\n th: {\n zIndex: 100,\n maxHeight: '250px',\n },\n }}\n >\n <table style={{ borderCollapse: 'collapse', width: '100%' }}>\n <thead>\n {table.getHeaderGroups().map(headerGroup => (\n <tr key={headerGroup.id}>\n {headerGroup.headers.map(header => {\n let columnSize: string = '5%'\n switch (header.id) {\n case 'name':\n columnSize = '50%'\n break\n case 'id':\n columnSize = '22%'\n break\n case 'type':\n columnSize = '22%'\n break\n default:\n break\n }\n return (\n <th\n key={header.id}\n colSpan={header.colSpan}\n style={{\n width: columnSize,\n position: 'sticky',\n top: '0px',\n }}\n >\n {header.isPlaceholder ? null : (\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n }}\n >\n {flexRender(\n header.column.columnDef.header,\n header.getContext(),\n )}\n <Box\n sx={{\n mx: 'auto',\n }}\n />\n {header.column.getCanSort() && (\n <IconButton\n onClick={header.column.getToggleSortingHandler()}\n size={'small'}\n sx={{\n marginLeft: 'auto',\n marginRight: '16px',\n }}\n >\n <IconSvg\n icon={\n header.column.getIsSorted() === 'asc'\n ? 'sortUp'\n : 'sortDown'\n }\n wrap={false}\n fontSize={'inherit'}\n sx={{\n color: header.column.getIsSorted()\n ? 'primary.main'\n : 'grey.700',\n backgroundColor: 'none',\n }}\n />\n </IconButton>\n )}\n </Box>\n )}\n {header.column.getCanResize() && (\n <div\n className={`resizer ${\n header.column.getIsResizing() ? 'isResizing' : ''\n }`}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n />\n )}\n </th>\n )\n })}\n </tr>\n ))}\n </thead>\n <tbody>\n {table.getRowModel().rows.map(row => {\n return (\n <tr key={row.id} style={{ height: '30px' }}>\n {row.getVisibleCells().map(cell => {\n return (\n <td\n key={cell.id}\n style={{\n width: cell.column.getSize(),\n }}\n >\n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext(),\n )}\n </td>\n )\n })}\n </tr>\n )\n })}\n </tbody>\n </table>\n </StyledTableContainer>\n )}\n <EntityFinderModal\n configuration={entityFinderConfiguration}\n promptCopy={`Select ${pluralObjectName} to add to the Synapse ID list`}\n show={showEntityFinder}\n title={`Select ${pluralObjectName}`}\n confirmButtonCopy={`Add ${pluralObjectName}`}\n onConfirm={items => {\n if (hideTextFieldToPasteValue) {\n const newRefs = [...refsInState, ...items]\n setRefsInState(newRefs)\n onUpdate(newRefs)\n } else {\n const newEntityIDsArray = items.map(ref => ref.targetId)\n const newEntityIDsString =\n newEntityIDs.trim().length > 0\n ? newEntityIDs.concat(',')\n : newEntityIDs\n const newValue = newEntityIDsString.concat(\n newEntityIDsArray.join(','),\n )\n setNewEntityIDs(newValue)\n }\n setShowEntityFinder(false)\n }}\n onCancel={() => setShowEntityFinder(false)}\n />\n {isEditable && (\n <Box sx={{ marginTop: '10px' }}>\n {hideTextFieldToPasteValue && (\n <Button\n variant=\"outlined\"\n onClick={() => {\n setShowEntityFinder(true)\n }}\n startIcon={<AddAd />}\n disabled={disabled}\n >\n Add {pluralObjectName}\n </Button>\n )}\n {!hideTextFieldToPasteValue && (\n <>\n <InputLabel htmlFor=\"synIDs\">Add Synapse IDs</InputLabel>\n <Box\n sx={{ display: 'grid', gridTemplateColumns: 'auto 50px 150px' }}\n >\n <TextField\n id=\"synIDs\"\n name=\"synIDs\"\n fullWidth\n onChange={e => {\n setNewEntityIDs(e.target.value)\n }}\n value={newEntityIDs}\n placeholder=\"Enter a list of Synapse IDs (i.e. 'syn123, syn456')\"\n disabled={disabled}\n />\n <Box sx={{ padding: '5px 0px 0px 5px' }}>\n {/* Entity finder button. On select, append the selected entity ID to the newSynIDs list */}\n <Tooltip title=\"Add a Synapse ID to the list via the Entity Finder\">\n <IconButton\n disabled={disabled}\n onClick={() => {\n setShowEntityFinder(true)\n }}\n >\n <AddAd />\n </IconButton>\n </Tooltip>\n </Box>\n <Button\n variant=\"outlined\"\n onClick={addPastedValuesToArray}\n disabled={\n isLoading || newEntityIDs.trim().length == 0 || disabled\n }\n startIcon={<AddCircleTwoTone />}\n >\n Add {pluralObjectName}\n </Button>\n </Box>\n </>\n )}\n {parseErrors && parseErrors.length > 0 && (\n <Alert severity={'error'} sx={{ my: 2 }}>\n <AlertTitle>Parsing errors encountered:</AlertTitle>\n <ul>\n {parseErrors.map((error, index) => {\n return (\n <Typography\n component={parseErrors.length > 1 ? 'li' : 'span'}\n key={index}\n variant={'smallText1'}\n sx={{\n lineHeight: 1.5,\n }}\n >\n {error}\n </Typography>\n )\n })}\n </ul>\n </Alert>\n )}\n </Box>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EntityHeaderTable.js","names":[],"sources":["../../../src/components/EntityHeaderTable/EntityHeaderTable.tsx"],"sourcesContent":["import React from 'react'\nimport AddAd from '@/assets/icons/AddAd'\nimport { StyledTableContainer } from '@/components/styled/StyledTableContainer'\nimport { useGetEntityHeaders } from '@/synapse-queries/entity/useGetEntityHeaders'\nimport {\n entityTypeToFriendlyName,\n getEntityTypeFromHeader,\n normalizeSynPrefix,\n} from '@/utils/functions/EntityTypeUtils'\nimport { SYNAPSE_ENTITY_ID_REGEX } from '@/utils/functions/RegularExpressions'\nimport { AddCircleTwoTone } from '@mui/icons-material'\nimport {\n Alert,\n AlertTitle,\n Box,\n Button,\n IconButton,\n InputLabel,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport { EntityHeader, ReferenceList } from '@sage-bionetworks/synapse-types'\nimport {\n ColumnDef,\n ColumnFiltersState,\n flexRender,\n getCoreRowModel,\n getFacetedMinMaxValues,\n getFacetedRowModel,\n getFacetedUniqueValues,\n getFilteredRowModel,\n getSortedRowModel,\n useReactTable,\n} from '@tanstack/react-table'\nimport { noop, upperFirst } from 'lodash-es'\nimport papaparseLib from 'papaparse'\n// papaparse is CJS-only; pull named values from the default export.\nconst { parse } = papaparseLib\nimport pluralize from 'pluralize'\nimport { Fragment, useCallback, useEffect, useMemo, useState } from 'react'\nimport {\n EntityFinderModal,\n EntityFinderModalProps,\n} from '../EntityFinder/EntityFinderModal'\nimport { FinderScope } from '../EntityFinder/tree/EntityTree'\nimport { VersionSelectionType } from '../EntityFinder/VersionSelectionType'\nimport IconSvg from '../IconSvg'\nimport { SkeletonTable } from '../Skeleton'\nimport {\n CheckBoxCell,\n CheckBoxHeader,\n EntityHeaderIDCell,\n EntityHeaderNameCell,\n EntityHeaderTypeCell,\n} from './EntityHeaderTableCellRenderers'\nimport { Filter } from './Filter'\nimport { useEntityHeaderTableState } from './useEntityHeaderTableState'\n\nconst DEFAULT_FINDER_CONFIG: EntityFinderModalProps['configuration'] = {\n selectMultiple: true,\n versionSelection: VersionSelectionType.DISALLOWED,\n initialScope: FinderScope.ALL_PROJECTS,\n initialContainer: 'root',\n}\n\nexport type EntityHeaderTableProps = {\n references: ReferenceList\n isEditable: boolean\n disabled?: boolean\n onUpdate?: (updatedRefs: ReferenceList) => void // when the references are updated, EntityHeaderTable will call this function with the updated list\n removeSelectedRowsButtonText?: string\n onUpdateEntityIDsTextbox?: (value: string) => void // when the entity IDs text box is updated, this is called\n /* The word used to describe the items in the table. Default 'entity' */\n objectNameCopy?: string\n // If true, the text field where IDs are pasted is hidden, and confirming the entity finder will immediately call `onUpdate`\n hideTextFieldToPasteValue?: boolean\n entityFinderConfiguration?: EntityFinderModalProps['configuration']\n}\n\nconst UNMANAGEABLE_SUBJECT_COUNT = 10\n\n// extend EntityHeader to create dummy EntityHeader rows for those that the current user cannot view\nexport type EntityHeaderOrDummy = EntityHeader & { isDummy?: boolean }\n\n/**\n * Renders a sortable/filterable table for a set of entity references. If editable, onUpdate will be called back\n * on any entity added/removed.\n */\nexport const EntityHeaderTable = (\n props: EntityHeaderTableProps,\n): React.ReactNode => {\n const {\n references,\n isEditable,\n disabled,\n onUpdate = noop,\n removeSelectedRowsButtonText = 'Remove Selected Rows',\n onUpdateEntityIDsTextbox,\n objectNameCopy = 'entity',\n hideTextFieldToPasteValue = false,\n entityFinderConfiguration = DEFAULT_FINDER_CONFIG,\n } = props\n const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])\n const [showEntityFinder, setShowEntityFinder] = useState<boolean>(false)\n\n const {\n rowSelection,\n setRowSelection,\n refsInState,\n setRefsInState,\n newEntityIDs,\n setNewEntityIDs,\n parseErrors,\n setParseErrors,\n } = useEntityHeaderTableState(references, onUpdateEntityIDsTextbox, onUpdate)\n\n const setInvalidEntityIDError = useCallback(\n (invalidEntityIDs: string[]) => {\n setParseErrors([`Invalid Synapse ID(s): ${invalidEntityIDs.join(',')}`])\n },\n [setParseErrors],\n )\n\n const addRefsFromEntityIDs = useCallback(\n (entityIDs: string[]) => {\n const newReferences: ReferenceList = entityIDs.map(id => {\n return {\n targetId: id.trim(),\n }\n })\n setRefsInState([...refsInState, ...newReferences])\n },\n [refsInState, setRefsInState],\n )\n\n const addPastedValuesToArray = useCallback(() => {\n if (newEntityIDs) {\n if (newEntityIDs.includes(',')) {\n parse<string[]>(newEntityIDs, {\n complete: result => {\n if (result.errors.length > 0) {\n const newParseErrors = result.errors.map(\n parseError => parseError.message,\n )\n setParseErrors(newParseErrors)\n } else {\n const newParsedEntityIDs = result.data[0]\n const invalidEntityIDs = newParsedEntityIDs.filter(\n id => !id.trim().match(SYNAPSE_ENTITY_ID_REGEX),\n )\n if (invalidEntityIDs.length > 0) {\n setInvalidEntityIDError(invalidEntityIDs)\n } else {\n addRefsFromEntityIDs(newParsedEntityIDs)\n }\n }\n },\n })\n } else {\n // single item\n if (!newEntityIDs.trim().match(SYNAPSE_ENTITY_ID_REGEX)) {\n setInvalidEntityIDError([newEntityIDs])\n } else {\n addRefsFromEntityIDs([newEntityIDs])\n }\n }\n } else {\n setParseErrors([])\n setNewEntityIDs('')\n }\n }, [\n addRefsFromEntityIDs,\n newEntityIDs,\n setInvalidEntityIDError,\n setNewEntityIDs,\n setParseErrors,\n ])\n\n const pluralObjectName = upperFirst(pluralize(objectNameCopy))\n\n const selectColumns: ColumnDef<EntityHeaderOrDummy, any>[] = useMemo(\n () => [\n {\n id: 'select',\n header: CheckBoxHeader,\n cell: CheckBoxCell,\n },\n ],\n [],\n )\n\n const entityHeaderColumns: ColumnDef<EntityHeaderOrDummy, any>[] = useMemo(\n () => [\n {\n accessorFn: (row: EntityHeaderOrDummy) => row.name,\n id: 'name',\n cell: EntityHeaderNameCell,\n header: 'Name',\n },\n {\n accessorFn: (row: EntityHeaderOrDummy) => row.id,\n id: 'id',\n cell: EntityHeaderIDCell,\n header: 'SynID',\n },\n {\n accessorFn: (row: EntityHeaderOrDummy) =>\n row.isDummy\n ? '-'\n : entityTypeToFriendlyName(getEntityTypeFromHeader(row)),\n id: 'type',\n header: 'Type',\n cell: EntityHeaderTypeCell,\n filterFn: 'includesString',\n },\n ],\n [],\n )\n\n const columns = useMemo<ColumnDef<EntityHeaderOrDummy, any>[]>(\n () =>\n isEditable\n ? selectColumns.concat(entityHeaderColumns)\n : entityHeaderColumns,\n [entityHeaderColumns, isEditable, selectColumns],\n )\n const selectionCount = Object.keys(rowSelection).length\n const {\n data: results,\n isSuccess,\n isLoading,\n } = useGetEntityHeaders(refsInState, {\n throwOnError: true,\n })\n\n const data = useMemo(() => {\n //create dummy entries for values that were not returned by the getEntityHeaders call!\n const newData = results ? results?.results : []\n const newDataEntityIds = new Set()\n newData.forEach(entityHeader =>\n newDataEntityIds.add(normalizeSynPrefix(entityHeader.id)),\n )\n const missingRefs = refsInState.filter(\n ref => !newDataEntityIds.has(normalizeSynPrefix(ref.targetId)),\n )\n const dummyEntityHeaders: EntityHeaderOrDummy[] = missingRefs.map(ref => {\n return {\n id: ref.targetId,\n name: ref.targetId,\n benefactorId: -1,\n type: 'org.sagebionetworks.repo.model.Project',\n createdOn: '',\n modifiedOn: '',\n createdBy: '',\n modifiedBy: '',\n isLatestVersion: true,\n isDummy: true,\n }\n })\n return newData.concat(dummyEntityHeaders)\n }, [refsInState, results])\n const table = useReactTable({\n data,\n columns,\n state: {\n rowSelection,\n columnFilters,\n },\n enableRowSelection: isEditable,\n onRowSelectionChange: setRowSelection,\n onColumnFiltersChange: setColumnFilters,\n getCoreRowModel: getCoreRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n getSortedRowModel: getSortedRowModel(),\n getFacetedRowModel: getFacetedRowModel(),\n getFacetedUniqueValues: getFacetedUniqueValues(),\n getFacetedMinMaxValues: getFacetedMinMaxValues(),\n // debugTable: true,\n // debugHeaders: true,\n // debugColumns: false,\n columnResizeMode: 'onChange',\n })\n\n const onRemove = useCallback(() => {\n // rowSelection looks like {3: true. 5: true} where the key is the row index.\n // Create a new ReferenceList based on the entityHeaders in the current table.\n const updatedData = data.filter(\n (_value, index) => !(rowSelection[index] === true),\n )\n const newRowRefs: ReferenceList = updatedData.map(entityHeader => {\n return {\n targetId: entityHeader.id,\n }\n })\n setRefsInState(newRowRefs)\n }, [data, rowSelection, setRefsInState])\n\n const isSelection = selectionCount > 0\n const totalRowCount = data.length\n const filteredRowCount = table.getPrePaginationRowModel().rows.length\n const showFilterControls = totalRowCount > UNMANAGEABLE_SUBJECT_COUNT\n\n /**\n * Reset the column filters when the filter controls are hidden.\n * This handles the following edge case:\n * 1. List contains 100 items of type \"A\" and 1 of type \"B\"\n * 2. User filters to show just \"A\" items\n * 3. User removes all \"A\" items\n * 4. Only the single \"B\" item remains, but the filter is still present on type \"A\".\n * The filter controls are hidden, so the user cannot see the \"B\" item.\n *\n * This effect will clear the filters when the filter controls are hidden, preventing this scenario.\n */\n useEffect(\n function resetFiltersWhenFilterControlsAreHidden() {\n if (!showFilterControls) {\n table.setColumnFilters([])\n }\n },\n [table, showFilterControls],\n )\n\n if (isLoading) {\n return (\n <SkeletonTable numCols={3} numRows={Math.min(10, refsInState.length)} />\n )\n } else if (!isSuccess) {\n return <></>\n }\n return (\n <div>\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'space-between',\n p: '12px 10px 10px 5px',\n }}\n >\n {showFilterControls && (\n <Typography variant=\"body1\" sx={{ marginBottom: '10px' }}>\n {totalRowCount} {pluralObjectName}{' '}\n {filteredRowCount < totalRowCount\n ? `(${filteredRowCount} visible)`\n : ''}\n {isSelection && <span>{` (${selectionCount} selected)`}</span>}\n </Typography>\n )}\n {isEditable && refsInState.length > 0 && (\n <Button\n variant=\"contained\"\n disabled={!isSelection || disabled}\n onClick={onRemove}\n >\n {removeSelectedRowsButtonText}\n </Button>\n )}\n </Box>\n <Box\n sx={{\n display: 'flex',\n pb: 2,\n }}\n >\n {table.getHeaderGroups().map(headerGroup =>\n headerGroup.headers.map(header => {\n return header.isPlaceholder ? null : (\n <Fragment key={header.column.id}>\n {header.column.getCanFilter() && showFilterControls ? (\n <Box sx={{ flexGrow: 1 }}>\n <Filter column={header.column} table={table} />\n </Box>\n ) : null}\n </Fragment>\n )\n }),\n )}\n </Box>\n {totalRowCount > 0 && (\n <StyledTableContainer\n sx={{\n th: {\n zIndex: 100,\n maxHeight: '250px',\n },\n }}\n >\n <table style={{ borderCollapse: 'collapse', width: '100%' }}>\n <thead>\n {table.getHeaderGroups().map(headerGroup => (\n <tr key={headerGroup.id}>\n {headerGroup.headers.map(header => {\n let columnSize: string = '5%'\n switch (header.id) {\n case 'name':\n columnSize = '50%'\n break\n case 'id':\n columnSize = '22%'\n break\n case 'type':\n columnSize = '22%'\n break\n default:\n break\n }\n return (\n <th\n key={header.id}\n colSpan={header.colSpan}\n style={{\n width: columnSize,\n position: 'sticky',\n top: '0px',\n }}\n >\n {header.isPlaceholder ? null : (\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n }}\n >\n {flexRender(\n header.column.columnDef.header,\n header.getContext(),\n )}\n <Box\n sx={{\n mx: 'auto',\n }}\n />\n {header.column.getCanSort() && (\n <IconButton\n onClick={header.column.getToggleSortingHandler()}\n size={'small'}\n sx={{\n marginLeft: 'auto',\n marginRight: '16px',\n }}\n >\n <IconSvg\n icon={\n header.column.getIsSorted() === 'asc'\n ? 'sortUp'\n : 'sortDown'\n }\n wrap={false}\n fontSize={'inherit'}\n sx={{\n color: header.column.getIsSorted()\n ? 'primary.main'\n : 'grey.700',\n backgroundColor: 'none',\n }}\n />\n </IconButton>\n )}\n </Box>\n )}\n {header.column.getCanResize() && (\n <div\n className={`resizer ${\n header.column.getIsResizing() ? 'isResizing' : ''\n }`}\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n />\n )}\n </th>\n )\n })}\n </tr>\n ))}\n </thead>\n <tbody>\n {table.getRowModel().rows.map(row => {\n return (\n <tr key={row.id} style={{ height: '30px' }}>\n {row.getVisibleCells().map(cell => {\n return (\n <td\n key={cell.id}\n style={{\n width: cell.column.getSize(),\n }}\n >\n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext(),\n )}\n </td>\n )\n })}\n </tr>\n )\n })}\n </tbody>\n </table>\n </StyledTableContainer>\n )}\n <EntityFinderModal\n configuration={entityFinderConfiguration}\n promptCopy={`Select ${pluralObjectName} to add to the Synapse ID list`}\n show={showEntityFinder}\n title={`Select ${pluralObjectName}`}\n confirmButtonCopy={`Add ${pluralObjectName}`}\n onConfirm={items => {\n if (hideTextFieldToPasteValue) {\n const newRefs = [...refsInState, ...items]\n setRefsInState(newRefs)\n onUpdate(newRefs)\n } else {\n const newEntityIDsArray = items.map(ref => ref.targetId)\n const newEntityIDsString =\n newEntityIDs.trim().length > 0\n ? newEntityIDs.concat(',')\n : newEntityIDs\n const newValue = newEntityIDsString.concat(\n newEntityIDsArray.join(','),\n )\n setNewEntityIDs(newValue)\n }\n setShowEntityFinder(false)\n }}\n onCancel={() => setShowEntityFinder(false)}\n />\n {isEditable && (\n <Box sx={{ marginTop: '10px' }}>\n {hideTextFieldToPasteValue && (\n <Button\n variant=\"outlined\"\n onClick={() => {\n setShowEntityFinder(true)\n }}\n startIcon={<AddAd />}\n disabled={disabled}\n >\n Add {pluralObjectName}\n </Button>\n )}\n {!hideTextFieldToPasteValue && (\n <>\n <InputLabel htmlFor=\"synIDs\">Add Synapse IDs</InputLabel>\n <Box\n sx={{ display: 'grid', gridTemplateColumns: 'auto 50px 150px' }}\n >\n <TextField\n id=\"synIDs\"\n name=\"synIDs\"\n fullWidth\n onChange={e => {\n setNewEntityIDs(e.target.value)\n }}\n value={newEntityIDs}\n placeholder=\"Enter a list of Synapse IDs (i.e. 'syn123, syn456')\"\n disabled={disabled}\n />\n <Box sx={{ padding: '5px 0px 0px 5px' }}>\n {/* Entity finder button. On select, append the selected entity ID to the newSynIDs list */}\n <Tooltip title=\"Add a Synapse ID to the list via the Entity Finder\">\n <IconButton\n disabled={disabled}\n onClick={() => {\n setShowEntityFinder(true)\n }}\n >\n <AddAd />\n </IconButton>\n </Tooltip>\n </Box>\n <Button\n variant=\"outlined\"\n onClick={addPastedValuesToArray}\n disabled={\n isLoading || newEntityIDs.trim().length == 0 || disabled\n }\n startIcon={<AddCircleTwoTone />}\n >\n Add {pluralObjectName}\n </Button>\n </Box>\n </>\n )}\n {parseErrors && parseErrors.length > 0 && (\n <Alert severity={'error'} sx={{ my: 2 }}>\n <AlertTitle>Parsing errors encountered:</AlertTitle>\n <ul>\n {parseErrors.map((error, index) => {\n return (\n <Typography\n component={parseErrors.length > 1 ? 'li' : 'span'}\n key={index}\n variant={'smallText1'}\n sx={{\n lineHeight: 1.5,\n }}\n >\n {error}\n </Typography>\n )\n })}\n </ul>\n </Alert>\n )}\n </Box>\n )}\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsCA,IAAM,EAAE,aAAU,GAqBZ,IAAiE;CACrE,gBAAgB;CAChB,kBAAkB,EAAqB;CACvC,cAAc,EAAY;CAC1B,kBAAkB;CACnB,EAgBK,IAA6B,IAStB,KACX,MACoB;CACpB,IAAM,EACJ,eACA,eACA,aACA,cAAW,IACX,kCAA+B,wBAC/B,8BACA,qBAAiB,UACjB,+BAA4B,IAC5B,gCAA4B,MAC1B,GACE,CAAC,IAAe,MAAoB,EAA6B,EAAE,CAAC,EACpE,CAAC,IAAkB,KAAuB,EAAkB,GAAM,EAElE,EACJ,iBACA,qBACA,gBACA,mBACA,iBACA,oBACA,gBACA,sBACE,EAA0B,GAAY,IAA0B,EAAS,EAEvE,IAA0B,GAC7B,MAA+B;AAC9B,IAAe,CAAC,0BAA0B,EAAiB,KAAK,IAAI,GAAG,CAAC;IAE1E,CAAC,EAAe,CACjB,EAEK,IAAuB,GAC1B,MAAwB;EACvB,IAAM,IAA+B,EAAU,KAAI,OAC1C,EACL,UAAU,EAAG,MAAM,EACpB,EACD;AACF,IAAe,CAAC,GAAG,GAAa,GAAG,EAAc,CAAC;IAEpD,CAAC,GAAa,EAAe,CAC9B,EAEK,KAAyB,QAAkB;AAC/C,EAAI,IACE,EAAa,SAAS,IAAI,GAC5B,EAAgB,GAAc,EAC5B,WAAU,MAAU;AAClB,OAAI,EAAO,OAAO,SAAS,EAIzB,GAHuB,EAAO,OAAO,KACnC,MAAc,EAAW,QAEZ,CAAe;QACzB;IACL,IAAM,IAAqB,EAAO,KAAK,IACjC,IAAmB,EAAmB,QAC1C,MAAM,CAAC,EAAG,MAAM,CAAC,MAAM,EAAwB,CAChD;AACD,IAAI,EAAiB,SAAS,IAC5B,EAAwB,EAAiB,GAEzC,EAAqB,EAAmB;;KAI/C,CAAC,GAGG,EAAa,MAAM,CAAC,MAAM,EAAwB,GAGrD,EAAqB,CAAC,EAAa,CAAC,GAFpC,EAAwB,CAAC,EAAa,CAAC,IAM3C,EAAe,EAAE,CAAC,EAClB,EAAgB,GAAG;IAEpB;EACD;EACA;EACA;EACA;EACA;EACD,CAAC,EAEI,IAAmB,GAAW,GAAU,GAAe,CAAC,EAExD,IAAuD,QACrD,CACJ;EACE,IAAI;EACJ,QAAQ;EACR,MAAM;EACP,CACF,EACD,EAAE,CACH,EAEK,IAA6D,QAC3D;EACJ;GACE,aAAa,MAA6B,EAAI;GAC9C,IAAI;GACJ,MAAM;GACN,QAAQ;GACT;EACD;GACE,aAAa,MAA6B,EAAI;GAC9C,IAAI;GACJ,MAAM;GACN,QAAQ;GACT;EACD;GACE,aAAa,MACX,EAAI,UACA,MACA,EAAyB,EAAwB,EAAI,CAAC;GAC5D,IAAI;GACJ,QAAQ;GACR,MAAM;GACN,UAAU;GACX;EACF,EACD,EAAE,CACH,EAEK,KAAU,QAEZ,IACI,EAAc,OAAO,EAAoB,GACzC,GACN;EAAC;EAAqB;EAAY;EAAc,CACjD,EACK,IAAiB,OAAO,KAAK,EAAa,CAAC,QAC3C,EACJ,MAAM,GACN,eACA,iBACE,GAAoB,GAAa,EACnC,cAAc,IACf,CAAC,EAEI,IAAO,QAAc;EAEzB,IAAM,IAAU,IAAU,GAAS,UAAU,EAAE,EACzC,oBAAmB,IAAI,KAAK;AAClC,IAAQ,SAAQ,MACd,EAAiB,IAAI,EAAmB,EAAa,GAAG,CAAC,CAC1D;EAID,IAAM,IAHc,EAAY,QAC9B,MAAO,CAAC,EAAiB,IAAI,EAAmB,EAAI,SAAS,CAAC,CAEd,CAAY,KAAI,OACzD;GACL,IAAI,EAAI;GACR,MAAM,EAAI;GACV,cAAc;GACd,MAAM;GACN,WAAW;GACX,YAAY;GACZ,WAAW;GACX,YAAY;GACZ,iBAAiB;GACjB,SAAS;GACV,EACD;AACF,SAAO,EAAQ,OAAO,EAAmB;IACxC,CAAC,GAAa,EAAQ,CAAC,EACpB,IAAQ,EAAc;EAC1B;EACA;EACA,OAAO;GACL;GACA;GACD;EACD,oBAAoB;EACpB,sBAAsB;EACtB,uBAAuB;EACvB,iBAAiB,IAAiB;EAClC,qBAAqB,IAAqB;EAC1C,mBAAmB,GAAmB;EACtC,oBAAoB,IAAoB;EACxC,wBAAwB,IAAwB;EAChD,wBAAwB,IAAwB;EAIhD,kBAAkB;EACnB,CAAC,EAEI,KAAW,QAAkB;AAWjC,IARoB,EAAK,QACtB,GAAQ,MAAY,EAAa,OAAW,GAEb,CAAY,KAAI,OACzC,EACL,UAAU,EAAa,IACxB,EAEY,CAAW;IACzB;EAAC;EAAM;EAAc;EAAe,CAAC,EAElC,IAAc,IAAiB,GAC/B,IAAgB,EAAK,QACrB,IAAmB,EAAM,0BAA0B,CAAC,KAAK,QACzD,IAAqB,IAAgB;AA6B3C,QAhBA,EACE,WAAmD;AACjD,EAAK,KACH,EAAM,iBAAiB,EAAE,CAAC;IAG9B,CAAC,GAAO,EAAmB,CAC5B,EAEG,IAEA,kBAAC,IAAD;EAAe,SAAS;EAAG,SAAS,KAAK,IAAI,IAAI,EAAY,OAAO;EAAI,CAAA,GAEhE,KAIV,kBAAC,OAAD,EAAA,UAAA;EACE,kBAAC,GAAD;GACE,IAAI;IACF,SAAS;IACT,gBAAgB;IAChB,GAAG;IACJ;aALH,CAOG,KACC,kBAAC,GAAD;IAAY,SAAQ;IAAQ,IAAI,EAAE,cAAc,QAAQ;cAAxD;KACG;KAAc;KAAE;KAAkB;KAClC,IAAmB,IAChB,IAAI,EAAiB,aACrB;KACH,KAAe,kBAAC,QAAD,EAAA,UAAO,KAAK,EAAe,aAAmB,CAAA;KACnD;OAEd,KAAc,EAAY,SAAS,KAClC,kBAAC,GAAD;IACE,SAAQ;IACR,UAAU,CAAC,KAAe;IAC1B,SAAS;cAER;IACM,CAAA,CAEP;;EACN,kBAAC,GAAD;GACE,IAAI;IACF,SAAS;IACT,IAAI;IACL;aAEA,EAAM,iBAAiB,CAAC,KAAI,MAC3B,EAAY,QAAQ,KAAI,MACf,EAAO,gBAAgB,OAC5B,kBAAC,GAAD,EAAA,UACG,EAAO,OAAO,cAAc,IAAI,IAC/B,kBAAC,GAAD;IAAK,IAAI,EAAE,UAAU,GAAG;cACtB,kBAAC,IAAD;KAAQ,QAAQ,EAAO;KAAe;KAAS,CAAA;IAC3C,CAAA,GACJ,MACK,EANI,EAAO,OAAO,GAMlB,CAEb,CACH;GACG,CAAA;EACL,IAAgB,KACf,kBAAC,GAAD;GACE,IAAI,EACF,IAAI;IACF,QAAQ;IACR,WAAW;IACZ,EACF;aAED,kBAAC,SAAD;IAAO,OAAO;KAAE,gBAAgB;KAAY,OAAO;KAAQ;cAA3D,CACE,kBAAC,SAAD,EAAA,UACG,EAAM,iBAAiB,CAAC,KAAI,MAC3B,kBAAC,MAAD,EAAA,UACG,EAAY,QAAQ,KAAI,MAAU;KACjC,IAAI,IAAqB;AACzB,aAAQ,EAAO,IAAf;MACE,KAAK;AACH,WAAa;AACb;MACF,KAAK;AACH,WAAa;AACb;MACF,KAAK;AACH,WAAa;AACb;MACF,QACE;;AAEJ,YACE,kBAAC,MAAD;MAEE,SAAS,EAAO;MAChB,OAAO;OACL,OAAO;OACP,UAAU;OACV,KAAK;OACN;gBAPH,CASG,EAAO,gBAAgB,OACtB,kBAAC,GAAD;OACE,IAAI;QACF,SAAS;QACT,YAAY;QACb;iBAJH;QAMG,EACC,EAAO,OAAO,UAAU,QACxB,EAAO,YAAY,CACpB;QACD,kBAAC,GAAD,EACE,IAAI,EACF,IAAI,QACL,EACD,CAAA;QACD,EAAO,OAAO,YAAY,IACzB,kBAAC,GAAD;SACE,SAAS,EAAO,OAAO,yBAAyB;SAChD,MAAM;SACN,IAAI;UACF,YAAY;UACZ,aAAa;UACd;mBAED,kBAAC,IAAD;UACE,MACE,EAAO,OAAO,aAAa,KAAK,QAC5B,WACA;UAEN,MAAM;UACN,UAAU;UACV,IAAI;WACF,OAAO,EAAO,OAAO,aAAa,GAC9B,iBACA;WACJ,iBAAiB;WAClB;UACD,CAAA;SACS,CAAA;QAEX;UAEP,EAAO,OAAO,cAAc,IAC3B,kBAAC,OAAD;OACE,WAAW,WACT,EAAO,OAAO,eAAe,GAAG,eAAe;OAEjD,aAAa,EAAO,kBAAkB;OACtC,cAAc,EAAO,kBAAkB;OACvC,CAAA,CAED;QA7DE,EAAO,GA6DT;MAEP,EACC,EAlFI,EAAY,GAkFhB,CACL,EACI,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAM,aAAa,CAAC,KAAK,KAAI,MAE1B,kBAAC,MAAD;KAAiB,OAAO,EAAE,QAAQ,QAAQ;eACvC,EAAI,iBAAiB,CAAC,KAAI,MAEvB,kBAAC,MAAD;MAEE,OAAO,EACL,OAAO,EAAK,OAAO,SAAS,EAC7B;gBAEA,EACC,EAAK,OAAO,UAAU,MACtB,EAAK,YAAY,CAClB;MACE,EATE,EAAK,GASP,CAEP;KACC,EAhBI,EAAI,GAgBR,CAEP,EACI,CAAA,CACF;;GACa,CAAA;EAEzB,kBAAC,IAAD;GACE,eAAe;GACf,YAAY,UAAU,EAAiB;GACvC,MAAM;GACN,OAAO,UAAU;GACjB,mBAAmB,OAAO;GAC1B,YAAW,MAAS;AAClB,QAAI,GAA2B;KAC7B,IAAM,IAAU,CAAC,GAAG,GAAa,GAAG,EAAM;AAE1C,KADA,EAAe,EAAQ,EACvB,EAAS,EAAQ;WACZ;KACL,IAAM,IAAoB,EAAM,KAAI,MAAO,EAAI,SAAS;AAQxD,QANE,EAAa,MAAM,CAAC,SAAS,IACzB,EAAa,OAAO,IAAI,GACxB,GAC8B,OAClC,EAAkB,KAAK,IAAI,CAEb,CAAS;;AAE3B,MAAoB,GAAM;;GAE5B,gBAAgB,EAAoB,GAAM;GAC1C,CAAA;EACD,KACC,kBAAC,GAAD;GAAK,IAAI,EAAE,WAAW,QAAQ;aAA9B;IACG,KACC,kBAAC,GAAD;KACE,SAAQ;KACR,eAAe;AACb,QAAoB,GAAK;;KAE3B,WAAW,kBAAC,GAAD,EAAS,CAAA;KACV;eANZ,CAOC,QACM,EACE;;IAEV,CAAC,KACA,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,IAAD;KAAY,SAAQ;eAAS;KAA4B,CAAA,EACzD,kBAAC,GAAD;KACE,IAAI;MAAE,SAAS;MAAQ,qBAAqB;MAAmB;eADjE;MAGE,kBAAC,IAAD;OACE,IAAG;OACH,MAAK;OACL,WAAA;OACA,WAAU,MAAK;AACb,UAAgB,EAAE,OAAO,MAAM;;OAEjC,OAAO;OACP,aAAY;OACF;OACV,CAAA;MACF,kBAAC,GAAD;OAAK,IAAI,EAAE,SAAS,mBAAmB;iBAErC,kBAAC,IAAD;QAAS,OAAM;kBACb,kBAAC,GAAD;SACY;SACV,eAAe;AACb,YAAoB,GAAK;;mBAG3B,kBAAC,GAAD,EAAS,CAAA;SACE,CAAA;QACL,CAAA;OACN,CAAA;MACN,kBAAC,GAAD;OACE,SAAQ;OACR,SAAS;OACT,UACE,KAAa,EAAa,MAAM,CAAC,UAAU,KAAK;OAElD,WAAW,kBAAC,IAAD,EAAoB,CAAA;iBANjC,CAOC,QACM,EACE;;MACL;OACL,EAAA,CAAA;IAEJ,KAAe,EAAY,SAAS,KACnC,kBAAC,IAAD;KAAO,UAAU;KAAS,IAAI,EAAE,IAAI,GAAG;eAAvC,CACE,kBAAC,IAAD,EAAA,UAAY,+BAAwC,CAAA,EACpD,kBAAC,MAAD,EAAA,UACG,EAAY,KAAK,GAAO,MAErB,kBAAC,GAAD;MACE,WAAW,EAAY,SAAS,IAAI,OAAO;MAE3C,SAAS;MACT,IAAI,EACF,YAAY,KACb;gBAEA;MACU,EAPN,EAOM,CAEf,EACC,CAAA,CACC;;IAEN;;EAEJ,EAAA,CAAA,GAvRC,kBAAA,GAAA,EAAK,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Filter.js","names":[],"sources":["../../../src/components/EntityHeaderTable/Filter.tsx"],"sourcesContent":["import { useMemo } from 'react'\nimport { Column, Table } from '@tanstack/react-table'\nimport { DebouncedInput } from './DebouncedInput'\n\nexport function Filter({\n column,\n table,\n}: {\n column: Column<any, unknown>\n table: Table<any>\n}) {\n const firstValue = table\n .getPreFilteredRowModel()\n .flatRows[0]?.getValue(column.id)\n\n const columnFilterValue = (column.getFilterValue() as string) ?? ''\n\n const sortedUniqueValues: string[] = useMemo(\n () =>\n typeof firstValue === 'number'\n ? []\n : Array.from(column.getFacetedUniqueValues().keys()).sort(),\n [column.getFacetedUniqueValues()],\n )\n return (\n <DebouncedInput\n type=\"text\"\n options={sortedUniqueValues}\n initialValue={columnFilterValue}\n onChange={value => column.setFilterValue(value)}\n label={`Filter by ${column.columnDef.header} (${\n column.getFacetedUniqueValues().size\n })`}\n />\n )\n}\n"],"mappings":";;;;AAIA,SAAgB,EAAO,EACrB,WACA,YAIC;CACD,IAAM,IAAa,EAChB,wBAAwB,CACxB,SAAS,IAAI,SAAS,EAAO,GAAG,EAE7B,IAAqB,EAAO,gBAAgB,IAAe;AASjE,QACE,kBAAC,GAAD;EACE,MAAK;EACL,SAViC,QAEjC,OAAO,KAAe,WAClB,EAAE,GACF,MAAM,KAAK,EAAO,wBAAwB,CAAC,MAAM,CAAC,CAAC,MAAM,EAC/D,CAAC,EAAO,wBAAwB,CAAC,
|
|
1
|
+
{"version":3,"file":"Filter.js","names":[],"sources":["../../../src/components/EntityHeaderTable/Filter.tsx"],"sourcesContent":["import { useMemo } from 'react'\nimport { Column, Table } from '@tanstack/react-table'\nimport { DebouncedInput } from './DebouncedInput'\n\nexport function Filter({\n column,\n table,\n}: {\n column: Column<any, unknown>\n table: Table<any>\n}) {\n const firstValue = table\n .getPreFilteredRowModel()\n .flatRows[0]?.getValue(column.id)\n\n const columnFilterValue = (column.getFilterValue() as string) ?? ''\n\n const sortedUniqueValues: string[] = useMemo(\n () =>\n typeof firstValue === 'number'\n ? []\n : Array.from(column.getFacetedUniqueValues().keys()).sort(),\n [column.getFacetedUniqueValues()],\n )\n return (\n <DebouncedInput\n type=\"text\"\n options={sortedUniqueValues}\n initialValue={columnFilterValue}\n onChange={value => column.setFilterValue(value)}\n label={`Filter by ${column.columnDef.header} (${\n column.getFacetedUniqueValues().size\n })`}\n />\n )\n}\n"],"mappings":";;;;AAIA,SAAgB,EAAO,EACrB,WACA,YAIC;CACD,IAAM,IAAa,EAChB,wBAAwB,CACxB,SAAS,IAAI,SAAS,EAAO,GAAG,EAE7B,IAAqB,EAAO,gBAAgB,IAAe;AASjE,QACE,kBAAC,GAAD;EACE,MAAK;EACL,SAViC,QAEjC,OAAO,KAAe,WAClB,EAAE,GACF,MAAM,KAAK,EAAO,wBAAwB,CAAC,MAAM,CAAC,CAAC,MAAM,EAC/D,CAAC,EAAO,wBAAwB,CAAC,CAKtB;EACT,cAAc;EACd,WAAU,MAAS,EAAO,eAAe,EAAM;EAC/C,OAAO,aAAa,EAAO,UAAU,OAAO,IAC1C,EAAO,wBAAwB,CAAC,KACjC;EACD,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEntityHeaderTableState.js","names":[],"sources":["../../../src/components/EntityHeaderTable/useEntityHeaderTableState.ts"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react'\nimport { RowSelectionState } from '@tanstack/react-table'\nimport { cloneDeep } from 'lodash-es'\nimport { ReferenceList } from '@sage-bionetworks/synapse-types'\n\nexport function useEntityHeaderTableState(\n references: ReferenceList,\n onUpdateEntityIDsTextbox?: (value: string) => void,\n onUpdate?: (updatedRefs: ReferenceList) => void,\n) {\n const [rowSelection, setRowSelection] = useState<RowSelectionState>({})\n const [refsInState, _setRefsInState] = useState<ReferenceList>(\n cloneDeep(references),\n )\n const [newEntityIDs, _setNewEntityIDs] = useState<string>('')\n const [parseErrors, setParseErrors] = useState<string[]>([])\n\n /* Reset state if the `references` argument changes */\n useEffect(() => {\n _setRefsInState(cloneDeep(references))\n }, [references])\n\n const setNewEntityIDs = useCallback(\n (newValue: string) => {\n _setNewEntityIDs(newValue)\n if (onUpdateEntityIDsTextbox) {\n onUpdateEntityIDsTextbox(newValue)\n }\n },\n [onUpdateEntityIDsTextbox],\n )\n\n const setRefsInState = useCallback(\n (refs: ReferenceList) => {\n setRowSelection({})\n _setRefsInState(refs)\n if (onUpdate) {\n onUpdate(refs)\n }\n setParseErrors([])\n setNewEntityIDs('')\n },\n [onUpdate, setNewEntityIDs],\n )\n\n return {\n rowSelection,\n setRowSelection,\n refsInState,\n setRefsInState,\n newEntityIDs,\n setNewEntityIDs,\n parseErrors,\n setParseErrors,\n }\n}\n"],"mappings":";;;AAKA,SAAgB,EACd,GACA,GACA,GACA;CACA,IAAM,CAAC,GAAc,KAAmB,EAA4B,EAAE,CAAC,EACjE,CAAC,GAAa,KAAmB,EACrC,EAAU,EAAW,CACtB,EACK,CAAC,GAAc,KAAoB,EAAiB,GAAG,EACvD,CAAC,GAAa,KAAkB,EAAmB,EAAE,CAAC;AAG5D,SAAgB;AACd,IAAgB,EAAU,EAAW,CAAC;IACrC,CAAC,EAAW,CAAC;CAEhB,IAAM,IAAkB,GACrB,MAAqB;AAEpB,EADA,EAAiB,EAAS,EACtB,KACF,EAAyB,EAAS;IAGtC,CAAC,EAAyB,CAC3B;AAeD,QAAO;EACL;EACA;EACA;EACA,gBAjBqB,GACpB,MAAwB;AAOvB,GANA,EAAgB,EAAE,CAAC,EACnB,EAAgB,EAAK,EACjB,KACF,EAAS,EAAK,EAEhB,EAAe,EAAE,CAAC,EAClB,EAAgB,GAAG;KAErB,CAAC,GAAU,EAAgB,
|
|
1
|
+
{"version":3,"file":"useEntityHeaderTableState.js","names":[],"sources":["../../../src/components/EntityHeaderTable/useEntityHeaderTableState.ts"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react'\nimport { RowSelectionState } from '@tanstack/react-table'\nimport { cloneDeep } from 'lodash-es'\nimport { ReferenceList } from '@sage-bionetworks/synapse-types'\n\nexport function useEntityHeaderTableState(\n references: ReferenceList,\n onUpdateEntityIDsTextbox?: (value: string) => void,\n onUpdate?: (updatedRefs: ReferenceList) => void,\n) {\n const [rowSelection, setRowSelection] = useState<RowSelectionState>({})\n const [refsInState, _setRefsInState] = useState<ReferenceList>(\n cloneDeep(references),\n )\n const [newEntityIDs, _setNewEntityIDs] = useState<string>('')\n const [parseErrors, setParseErrors] = useState<string[]>([])\n\n /* Reset state if the `references` argument changes */\n useEffect(() => {\n _setRefsInState(cloneDeep(references))\n }, [references])\n\n const setNewEntityIDs = useCallback(\n (newValue: string) => {\n _setNewEntityIDs(newValue)\n if (onUpdateEntityIDsTextbox) {\n onUpdateEntityIDsTextbox(newValue)\n }\n },\n [onUpdateEntityIDsTextbox],\n )\n\n const setRefsInState = useCallback(\n (refs: ReferenceList) => {\n setRowSelection({})\n _setRefsInState(refs)\n if (onUpdate) {\n onUpdate(refs)\n }\n setParseErrors([])\n setNewEntityIDs('')\n },\n [onUpdate, setNewEntityIDs],\n )\n\n return {\n rowSelection,\n setRowSelection,\n refsInState,\n setRefsInState,\n newEntityIDs,\n setNewEntityIDs,\n parseErrors,\n setParseErrors,\n }\n}\n"],"mappings":";;;AAKA,SAAgB,EACd,GACA,GACA,GACA;CACA,IAAM,CAAC,GAAc,KAAmB,EAA4B,EAAE,CAAC,EACjE,CAAC,GAAa,KAAmB,EACrC,EAAU,EAAW,CACtB,EACK,CAAC,GAAc,KAAoB,EAAiB,GAAG,EACvD,CAAC,GAAa,KAAkB,EAAmB,EAAE,CAAC;AAG5D,SAAgB;AACd,IAAgB,EAAU,EAAW,CAAC;IACrC,CAAC,EAAW,CAAC;CAEhB,IAAM,IAAkB,GACrB,MAAqB;AAEpB,EADA,EAAiB,EAAS,EACtB,KACF,EAAyB,EAAS;IAGtC,CAAC,EAAyB,CAC3B;AAeD,QAAO;EACL;EACA;EACA;EACA,gBAjBqB,GACpB,MAAwB;AAOvB,GANA,EAAgB,EAAE,CAAC,EACnB,EAAgB,EAAK,EACjB,KACF,EAAS,EAAK,EAEhB,EAAe,EAAE,CAAC,EAClB,EAAgB,GAAG;KAErB,CAAC,GAAU,EAAgB,CAO3B;EACA;EACA;EACA;EACA;EACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntitySubjectsSelector.js","names":[],"sources":["../../../src/components/EntitySubjectsSelector/EntitySubjectsSelector.tsx"],"sourcesContent":["import { Box, Typography } from '@mui/material'\nimport {\n Reference,\n ReferenceList,\n RestrictableObjectDescriptor,\n RestrictableObjectType,\n} from '@sage-bionetworks/synapse-types'\nimport { isEqual, uniqWith } from 'lodash-es'\nimport { useMemo } from 'react'\nimport EntityHeaderTable, { EntityHeaderTableProps } from '../EntityHeaderTable'\n\nexport const REMOVE_BUTTON_TEXT = 'Mark for Removal from AR'\nexport const NO_ENTITIES_SELECTED = 'No entities selected.'\n\nexport type EntitySubjectsSelectorProps = {\n // will be filtered to only entity subjects\n subjects: RestrictableObjectDescriptor[]\n onUpdate?: (subjects: RestrictableObjectDescriptor[]) => void\n onUpdateEntityIDsTextbox: EntityHeaderTableProps['onUpdateEntityIDsTextbox']\n}\n\nfunction EntitySubjectsSelector(props: EntitySubjectsSelectorProps) {\n const { subjects, onUpdate, onUpdateEntityIDsTextbox } = props\n\n const references = useMemo(() => {\n return subjects\n .filter(subject => {\n return subject.type === RestrictableObjectType.ENTITY\n })\n .map(subject => {\n const ref: Reference = {\n targetId: subject.id,\n }\n return ref\n })\n }, [subjects])\n\n const handleChange = (updatedReferences: ReferenceList) => {\n if (onUpdate) {\n const dedupedReferences = uniqWith(updatedReferences, isEqual)\n const updatedSubjects = dedupedReferences.map(reference => {\n const subject: RestrictableObjectDescriptor = {\n id: reference.targetId,\n type: RestrictableObjectType.ENTITY,\n }\n return subject\n })\n onUpdate(updatedSubjects)\n }\n }\n\n return (\n <Box\n sx={{\n mb: 2,\n }}\n >\n {references.length === 0 && (\n <Typography\n variant=\"body1Italic\"\n sx={{\n mb: -4,\n }}\n >\n {NO_ENTITIES_SELECTED}\n </Typography>\n )}\n <EntityHeaderTable\n references={references}\n isEditable={Boolean(onUpdate)}\n onUpdate={newReferences => {\n handleChange(newReferences)\n }}\n removeSelectedRowsButtonText={REMOVE_BUTTON_TEXT}\n onUpdateEntityIDsTextbox={onUpdateEntityIDsTextbox}\n />\n </Box>\n )\n}\n\nexport default EntitySubjectsSelector\n"],"mappings":";;;;;;;AAWA,IAAa,IAAqB,4BACrB,IAAuB;AASpC,SAAS,EAAuB,GAAoC;CAClE,IAAM,EAAE,aAAU,aAAU,gCAA6B,GAEnD,IAAa,QACV,EACJ,QAAO,MACC,EAAQ,SAAS,EAAuB,OAC/C,CACD,KAAI,
|
|
1
|
+
{"version":3,"file":"EntitySubjectsSelector.js","names":[],"sources":["../../../src/components/EntitySubjectsSelector/EntitySubjectsSelector.tsx"],"sourcesContent":["import { Box, Typography } from '@mui/material'\nimport {\n Reference,\n ReferenceList,\n RestrictableObjectDescriptor,\n RestrictableObjectType,\n} from '@sage-bionetworks/synapse-types'\nimport { isEqual, uniqWith } from 'lodash-es'\nimport { useMemo } from 'react'\nimport EntityHeaderTable, { EntityHeaderTableProps } from '../EntityHeaderTable'\n\nexport const REMOVE_BUTTON_TEXT = 'Mark for Removal from AR'\nexport const NO_ENTITIES_SELECTED = 'No entities selected.'\n\nexport type EntitySubjectsSelectorProps = {\n // will be filtered to only entity subjects\n subjects: RestrictableObjectDescriptor[]\n onUpdate?: (subjects: RestrictableObjectDescriptor[]) => void\n onUpdateEntityIDsTextbox: EntityHeaderTableProps['onUpdateEntityIDsTextbox']\n}\n\nfunction EntitySubjectsSelector(props: EntitySubjectsSelectorProps) {\n const { subjects, onUpdate, onUpdateEntityIDsTextbox } = props\n\n const references = useMemo(() => {\n return subjects\n .filter(subject => {\n return subject.type === RestrictableObjectType.ENTITY\n })\n .map(subject => {\n const ref: Reference = {\n targetId: subject.id,\n }\n return ref\n })\n }, [subjects])\n\n const handleChange = (updatedReferences: ReferenceList) => {\n if (onUpdate) {\n const dedupedReferences = uniqWith(updatedReferences, isEqual)\n const updatedSubjects = dedupedReferences.map(reference => {\n const subject: RestrictableObjectDescriptor = {\n id: reference.targetId,\n type: RestrictableObjectType.ENTITY,\n }\n return subject\n })\n onUpdate(updatedSubjects)\n }\n }\n\n return (\n <Box\n sx={{\n mb: 2,\n }}\n >\n {references.length === 0 && (\n <Typography\n variant=\"body1Italic\"\n sx={{\n mb: -4,\n }}\n >\n {NO_ENTITIES_SELECTED}\n </Typography>\n )}\n <EntityHeaderTable\n references={references}\n isEditable={Boolean(onUpdate)}\n onUpdate={newReferences => {\n handleChange(newReferences)\n }}\n removeSelectedRowsButtonText={REMOVE_BUTTON_TEXT}\n onUpdateEntityIDsTextbox={onUpdateEntityIDsTextbox}\n />\n </Box>\n )\n}\n\nexport default EntitySubjectsSelector\n"],"mappings":";;;;;;;AAWA,IAAa,IAAqB,4BACrB,IAAuB;AASpC,SAAS,EAAuB,GAAoC;CAClE,IAAM,EAAE,aAAU,aAAU,gCAA6B,GAEnD,IAAa,QACV,EACJ,QAAO,MACC,EAAQ,SAAS,EAAuB,OAC/C,CACD,KAAI,OAII,EAFL,UAAU,EAAQ,IAEb,EACP,EACH,CAAC,EAAS,CAAC,EAER,KAAgB,MAAqC;AACzD,EAAI,KASF,EAR0B,EAAS,GAAmB,EAC9B,CAAkB,KAAI,OAKrC;GAHL,IAAI,EAAU;GACd,MAAM,EAAuB;GAExB,EAEA,CAAgB;;AAI7B,QACE,kBAAC,GAAD;EACE,IAAI,EACF,IAAI,GACL;YAHH,CAKG,EAAW,WAAW,KACrB,kBAAC,GAAD;GACE,SAAQ;GACR,IAAI,EACF,IAAI,IACL;;GAGU,CAAA,EAEf,kBAAC,GAAD;GACc;GACZ,YAAY,EAAQ;GACpB,WAAU,MAAiB;AACzB,MAAa,EAAc;;GAE7B,8BAA8B;GACJ;GAC1B,CAAA,CACE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IdColumnHeader.js","names":[],"sources":["../../../../src/components/EntityTreeTable/components/IdColumnHeader.tsx"],"sourcesContent":["import React, { useMemo } from 'react'\nimport { HeaderContext } from '@tanstack/react-table'\nimport { EntityBundleRow } from '../EntityTreeTable'\nimport ColumnHeader from '../../TanStackTable/ColumnHeader'\nimport { CopyToClipboardIcon } from '../../CopyToClipboardIcon'\n\nexport const IdColumnHeader: React.FC<\n HeaderContext<EntityBundleRow, unknown>\n> = props => {\n const { table } = props\n\n // Get all visible rows and extract their entity IDs\n const visibleIds = useMemo(() => {\n const visibleRows = table.getRowModel().rows\n return visibleRows\n .filter(row => !row.original.isLoadMore && row.original.entityId) // Exclude \"Load More\" rows and rows without entityId\n .map(row => row.original.entityId)\n .join('\\n')\n }, [table.getRowModel().rows])\n\n const copyButton = <CopyToClipboardIcon value={visibleIds} sizePx={16} />\n\n return <ColumnHeader {...props} title=\"ID\" additionalButtons={copyButton} />\n}\n"],"mappings":";;;;;AAMA,IAAa,KAET,MAAS;CACX,IAAM,EAAE,aAAU,GAWZ,IAAa,kBAAC,GAAD;EAAqB,OARrB,QACG,EAAM,aAAa,CAAC,KAErC,QAAO,MAAO,CAAC,EAAI,SAAS,cAAc,EAAI,SAAS,SAAS,CAChE,KAAI,MAAO,EAAI,SAAS,SAAS,CACjC,KAAK,KAAK,EACZ,CAAC,EAAM,aAAa,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"IdColumnHeader.js","names":[],"sources":["../../../../src/components/EntityTreeTable/components/IdColumnHeader.tsx"],"sourcesContent":["import React, { useMemo } from 'react'\nimport { HeaderContext } from '@tanstack/react-table'\nimport { EntityBundleRow } from '../EntityTreeTable'\nimport ColumnHeader from '../../TanStackTable/ColumnHeader'\nimport { CopyToClipboardIcon } from '../../CopyToClipboardIcon'\n\nexport const IdColumnHeader: React.FC<\n HeaderContext<EntityBundleRow, unknown>\n> = props => {\n const { table } = props\n\n // Get all visible rows and extract their entity IDs\n const visibleIds = useMemo(() => {\n const visibleRows = table.getRowModel().rows\n return visibleRows\n .filter(row => !row.original.isLoadMore && row.original.entityId) // Exclude \"Load More\" rows and rows without entityId\n .map(row => row.original.entityId)\n .join('\\n')\n }, [table.getRowModel().rows])\n\n const copyButton = <CopyToClipboardIcon value={visibleIds} sizePx={16} />\n\n return <ColumnHeader {...props} title=\"ID\" additionalButtons={copyButton} />\n}\n"],"mappings":";;;;;AAMA,IAAa,KAET,MAAS;CACX,IAAM,EAAE,aAAU,GAWZ,IAAa,kBAAC,GAAD;EAAqB,OARrB,QACG,EAAM,aAAa,CAAC,KAErC,QAAO,MAAO,CAAC,EAAI,SAAS,cAAc,EAAI,SAAS,SAAS,CAChE,KAAI,MAAO,EAAI,SAAS,SAAS,CACjC,KAAK,KAAK,EACZ,CAAC,EAAM,aAAa,CAAC,KAAK,CAEkB;EAAY,QAAQ;EAAM,CAAA;AAEzE,QAAO,kBAAC,GAAD;EAAc,GAAI;EAAO,OAAM;EAAK,mBAAmB;EAAc,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEntityTreeState.js","names":[],"sources":["../../../../src/components/EntityTreeTable/hooks/useEntityTreeState.ts"],"sourcesContent":["import { useState, useCallback, useEffect, useMemo } from 'react'\nimport { SortingState } from '@tanstack/react-table'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { matchQuery } from '@tanstack/query-core'\nimport {\n EntityChildrenRequest,\n EntityHeader,\n SortBy,\n Direction,\n} from '@sage-bionetworks/synapse-types'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport { useGetEntityHeader } from '@/synapse-queries'\nimport { useGetEntityChildren } from '@/synapse-queries/entity/useGetEntityChildren'\nimport { convertToEntityType } from '@/utils/functions/EntityTypeUtils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\n\nexport type TreeNode = {\n entityHeader: EntityHeader\n parentId?: string\n depth: number\n isLeaf: boolean\n children?: TreeNode[]\n}\n\nconst includeTypes: EntityType[] = [\n EntityType.folder,\n EntityType.file,\n EntityType.link,\n EntityType.recordset,\n]\n\n/**\n * Hook that manages all tree state and data initialization.\n */\nexport const useEntityTreeState = (\n rootId: string,\n expandRootByDefault: boolean,\n showRootNode: boolean,\n) => {\n // Core state\n const [expanded, setExpanded] = useState<Record<string, boolean>>({})\n const [tree, setTree] = useState<Record<string, TreeNode>>({})\n const [loadingIds, setLoadingIds] = useState<Set<string>>(new Set())\n const [loadedChildren, setLoadedChildren] = useState<Set<string>>(new Set())\n const [nextPageTokens, setNextPageTokens] = useState<\n Record<string, string | undefined>\n >({})\n const [loadingPageTokens, setLoadingPageTokens] = useState<\n Record<string, string | undefined>\n >({})\n const [sorting, setSorting] = useState<SortingState>([])\n const queryClient = useQueryClient()\n const { keyFactory } = useSynapseContext()\n const rootEntityQueryKey = useMemo(\n () => keyFactory.getEntityQueryKey(rootId),\n [keyFactory, rootId],\n )\n\n // Derive sorting parameters from state\n const { sortBy, sortDirection } = useMemo(() => {\n if (!sorting.length) return {}\n\n const firstSort = sorting[0]\n let sortBy: SortBy | undefined\n\n switch (firstSort.id) {\n case 'name':\n sortBy = SortBy.NAME\n break\n case 'createdOn':\n sortBy = SortBy.CREATED_ON\n break\n case 'modifiedOn':\n sortBy = SortBy.MODIFIED_ON\n break\n default:\n return {}\n }\n\n return {\n sortBy,\n sortDirection: firstSort.desc ? Direction.DESC : Direction.ASC,\n }\n }, [sorting])\n\n const rootChildrenRequest: EntityChildrenRequest = useMemo(() => {\n const baseRequest: EntityChildrenRequest = {\n parentId: rootId,\n includeTypes,\n sortBy,\n sortDirection,\n }\n\n return baseRequest\n }, [rootId, sortBy, sortDirection])\n\n const rootChildrenQueryKey = useMemo(\n () => keyFactory.getEntityChildrenQueryKey(rootChildrenRequest, false),\n [keyFactory, rootChildrenRequest],\n )\n\n // Reset tree data when sorting changes\n const resetTreeData = useCallback(() => {\n void queryClient.removeQueries({\n queryKey: rootChildrenQueryKey,\n exact: true,\n })\n\n setTree({})\n setLoadedChildren(new Set())\n setExpanded({})\n setNextPageTokens({})\n setLoadingPageTokens({})\n setLoadingIds(new Set())\n }, [queryClient, rootChildrenQueryKey])\n\n // Get root entity header\n const { data: rootHeader } = useGetEntityHeader(rootId)\n // Get root children - only fetch when we have header and haven't loaded children yet\n const shouldFetchChildren = !!rootHeader && !loadedChildren.has(rootId)\n const { data: rootChildren } = useGetEntityChildren(rootChildrenRequest, {\n enabled: shouldFetchChildren,\n })\n\n // Effect to reset data when sorting changes\n useEffect(() => {\n resetTreeData()\n }, [resetTreeData, sorting])\n\n useEffect(() => {\n const unsubscribe = queryClient.getQueryCache().subscribe(event => {\n if (event?.type === 'updated' && event.action?.type === 'invalidate') {\n // Check if any entity in the tree was invalidated\n const entityIds = Object.keys(tree)\n for (const entityId of entityIds) {\n const entityQueryKey = keyFactory.getEntityQueryKey(entityId)\n if (\n matchQuery({ queryKey: entityQueryKey, exact: false }, event.query)\n ) {\n resetTreeData()\n return\n }\n }\n }\n })\n\n return () => {\n unsubscribe()\n }\n }, [queryClient, rootEntityQueryKey, resetTreeData, tree, keyFactory])\n\n // Effect to initialize root node and its children\n useEffect(() => {\n if (rootHeader && rootChildren && !tree[rootId]) {\n // Build the children nodes from the response\n const children: TreeNode[] = rootChildren.page.map(\n (eh: EntityHeader) => ({\n entityHeader: eh,\n parentId: rootId,\n depth: showRootNode ? 1 : 0,\n isLeaf: !(\n convertToEntityType(eh.type) === EntityType.project ||\n convertToEntityType(eh.type) === EntityType.folder\n ),\n }),\n )\n\n // Create child entries for the tree\n const childEntries = Object.fromEntries(\n children.map(child => [child.entityHeader.id, child]),\n )\n\n // Set up the complete tree state in one go\n setTree({\n [rootId]: {\n entityHeader: rootHeader,\n depth: showRootNode ? 0 : -1,\n isLeaf: false,\n children,\n },\n ...childEntries,\n })\n\n // Set up pagination token\n const rootNext = rootChildren.nextPageToken\n setNextPageTokens({ [rootId]: rootNext })\n\n // Mark as loaded if there's no next page token\n if (!rootNext) {\n setLoadedChildren(new Set([rootId]))\n }\n\n // Expand root node by default if the flag is set\n if (expandRootByDefault) {\n setExpanded(prev => ({ ...prev, [rootId]: true }))\n }\n }\n }, [\n rootHeader,\n rootChildren,\n rootId,\n expandRootByDefault,\n showRootNode,\n tree,\n ])\n\n return {\n // State\n expanded,\n setExpanded,\n tree,\n setTree,\n loadingIds,\n setLoadingIds,\n loadedChildren,\n setLoadedChildren,\n nextPageTokens,\n setNextPageTokens,\n loadingPageTokens,\n setLoadingPageTokens,\n sorting,\n setSorting,\n\n // Derived values\n sortBy,\n sortDirection,\n\n // Data\n rootHeader,\n rootChildren,\n\n // Actions\n resetTreeData,\n }\n}\n"],"mappings":";;;;;;;;;;;AAwBA,IAAM,IAA6B;CACjC,EAAW;CACX,EAAW;CACX,EAAW;CACX,EAAW;CACZ,EAKY,KACX,GACA,GACA,MACG;CAEH,IAAM,CAAC,GAAU,KAAe,EAAkC,EAAE,CAAC,EAC/D,CAAC,GAAM,KAAW,EAAmC,EAAE,CAAC,EACxD,CAAC,GAAY,KAAiB,kBAAsB,IAAI,KAAK,CAAC,EAC9D,CAAC,GAAgB,KAAqB,kBAAsB,IAAI,KAAK,CAAC,EACtE,CAAC,GAAgB,KAAqB,EAE1C,EAAE,CAAC,EACC,CAAC,GAAmB,KAAwB,EAEhD,EAAE,CAAC,EACC,CAAC,GAAS,KAAc,EAAuB,EAAE,CAAC,EAClD,IAAc,GAAgB,EAC9B,EAAE,kBAAe,GAAmB,EACpC,IAAqB,QACnB,EAAW,kBAAkB,EAAO,EAC1C,CAAC,GAAY,EAAO,CACrB,EAGK,EAAE,WAAQ,qBAAkB,QAAc;AAC9C,MAAI,CAAC,EAAQ,OAAQ,QAAO,EAAE;EAE9B,IAAM,IAAY,EAAQ,IACtB;AAEJ,UAAQ,EAAU,IAAlB;GACE,KAAK;AACH,QAAS,EAAO;AAChB;GACF,KAAK;AACH,QAAS,EAAO;AAChB;GACF,KAAK;AACH,QAAS,EAAO;AAChB;GACF,QACE,QAAO,EAAE;;AAGb,SAAO;GACL;GACA,eAAe,EAAU,OAAO,EAAU,OAAO,EAAU;GAC5D;IACA,CAAC,EAAQ,CAAC,EAEP,IAA6C,
|
|
1
|
+
{"version":3,"file":"useEntityTreeState.js","names":[],"sources":["../../../../src/components/EntityTreeTable/hooks/useEntityTreeState.ts"],"sourcesContent":["import { useState, useCallback, useEffect, useMemo } from 'react'\nimport { SortingState } from '@tanstack/react-table'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { matchQuery } from '@tanstack/query-core'\nimport {\n EntityChildrenRequest,\n EntityHeader,\n SortBy,\n Direction,\n} from '@sage-bionetworks/synapse-types'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport { useGetEntityHeader } from '@/synapse-queries'\nimport { useGetEntityChildren } from '@/synapse-queries/entity/useGetEntityChildren'\nimport { convertToEntityType } from '@/utils/functions/EntityTypeUtils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\n\nexport type TreeNode = {\n entityHeader: EntityHeader\n parentId?: string\n depth: number\n isLeaf: boolean\n children?: TreeNode[]\n}\n\nconst includeTypes: EntityType[] = [\n EntityType.folder,\n EntityType.file,\n EntityType.link,\n EntityType.recordset,\n]\n\n/**\n * Hook that manages all tree state and data initialization.\n */\nexport const useEntityTreeState = (\n rootId: string,\n expandRootByDefault: boolean,\n showRootNode: boolean,\n) => {\n // Core state\n const [expanded, setExpanded] = useState<Record<string, boolean>>({})\n const [tree, setTree] = useState<Record<string, TreeNode>>({})\n const [loadingIds, setLoadingIds] = useState<Set<string>>(new Set())\n const [loadedChildren, setLoadedChildren] = useState<Set<string>>(new Set())\n const [nextPageTokens, setNextPageTokens] = useState<\n Record<string, string | undefined>\n >({})\n const [loadingPageTokens, setLoadingPageTokens] = useState<\n Record<string, string | undefined>\n >({})\n const [sorting, setSorting] = useState<SortingState>([])\n const queryClient = useQueryClient()\n const { keyFactory } = useSynapseContext()\n const rootEntityQueryKey = useMemo(\n () => keyFactory.getEntityQueryKey(rootId),\n [keyFactory, rootId],\n )\n\n // Derive sorting parameters from state\n const { sortBy, sortDirection } = useMemo(() => {\n if (!sorting.length) return {}\n\n const firstSort = sorting[0]\n let sortBy: SortBy | undefined\n\n switch (firstSort.id) {\n case 'name':\n sortBy = SortBy.NAME\n break\n case 'createdOn':\n sortBy = SortBy.CREATED_ON\n break\n case 'modifiedOn':\n sortBy = SortBy.MODIFIED_ON\n break\n default:\n return {}\n }\n\n return {\n sortBy,\n sortDirection: firstSort.desc ? Direction.DESC : Direction.ASC,\n }\n }, [sorting])\n\n const rootChildrenRequest: EntityChildrenRequest = useMemo(() => {\n const baseRequest: EntityChildrenRequest = {\n parentId: rootId,\n includeTypes,\n sortBy,\n sortDirection,\n }\n\n return baseRequest\n }, [rootId, sortBy, sortDirection])\n\n const rootChildrenQueryKey = useMemo(\n () => keyFactory.getEntityChildrenQueryKey(rootChildrenRequest, false),\n [keyFactory, rootChildrenRequest],\n )\n\n // Reset tree data when sorting changes\n const resetTreeData = useCallback(() => {\n void queryClient.removeQueries({\n queryKey: rootChildrenQueryKey,\n exact: true,\n })\n\n setTree({})\n setLoadedChildren(new Set())\n setExpanded({})\n setNextPageTokens({})\n setLoadingPageTokens({})\n setLoadingIds(new Set())\n }, [queryClient, rootChildrenQueryKey])\n\n // Get root entity header\n const { data: rootHeader } = useGetEntityHeader(rootId)\n // Get root children - only fetch when we have header and haven't loaded children yet\n const shouldFetchChildren = !!rootHeader && !loadedChildren.has(rootId)\n const { data: rootChildren } = useGetEntityChildren(rootChildrenRequest, {\n enabled: shouldFetchChildren,\n })\n\n // Effect to reset data when sorting changes\n useEffect(() => {\n resetTreeData()\n }, [resetTreeData, sorting])\n\n useEffect(() => {\n const unsubscribe = queryClient.getQueryCache().subscribe(event => {\n if (event?.type === 'updated' && event.action?.type === 'invalidate') {\n // Check if any entity in the tree was invalidated\n const entityIds = Object.keys(tree)\n for (const entityId of entityIds) {\n const entityQueryKey = keyFactory.getEntityQueryKey(entityId)\n if (\n matchQuery({ queryKey: entityQueryKey, exact: false }, event.query)\n ) {\n resetTreeData()\n return\n }\n }\n }\n })\n\n return () => {\n unsubscribe()\n }\n }, [queryClient, rootEntityQueryKey, resetTreeData, tree, keyFactory])\n\n // Effect to initialize root node and its children\n useEffect(() => {\n if (rootHeader && rootChildren && !tree[rootId]) {\n // Build the children nodes from the response\n const children: TreeNode[] = rootChildren.page.map(\n (eh: EntityHeader) => ({\n entityHeader: eh,\n parentId: rootId,\n depth: showRootNode ? 1 : 0,\n isLeaf: !(\n convertToEntityType(eh.type) === EntityType.project ||\n convertToEntityType(eh.type) === EntityType.folder\n ),\n }),\n )\n\n // Create child entries for the tree\n const childEntries = Object.fromEntries(\n children.map(child => [child.entityHeader.id, child]),\n )\n\n // Set up the complete tree state in one go\n setTree({\n [rootId]: {\n entityHeader: rootHeader,\n depth: showRootNode ? 0 : -1,\n isLeaf: false,\n children,\n },\n ...childEntries,\n })\n\n // Set up pagination token\n const rootNext = rootChildren.nextPageToken\n setNextPageTokens({ [rootId]: rootNext })\n\n // Mark as loaded if there's no next page token\n if (!rootNext) {\n setLoadedChildren(new Set([rootId]))\n }\n\n // Expand root node by default if the flag is set\n if (expandRootByDefault) {\n setExpanded(prev => ({ ...prev, [rootId]: true }))\n }\n }\n }, [\n rootHeader,\n rootChildren,\n rootId,\n expandRootByDefault,\n showRootNode,\n tree,\n ])\n\n return {\n // State\n expanded,\n setExpanded,\n tree,\n setTree,\n loadingIds,\n setLoadingIds,\n loadedChildren,\n setLoadedChildren,\n nextPageTokens,\n setNextPageTokens,\n loadingPageTokens,\n setLoadingPageTokens,\n sorting,\n setSorting,\n\n // Derived values\n sortBy,\n sortDirection,\n\n // Data\n rootHeader,\n rootChildren,\n\n // Actions\n resetTreeData,\n }\n}\n"],"mappings":"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"}
|