synapse-react-client 4.0.9 → 4.0.10
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/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.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 +113 -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.js.map +1 -1
- 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/json-rx/JsonRx.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/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.js.map +1 -1
- 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.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/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.js.map +1 -1
- package/dist/components/Forum/DiscussionSearchResult.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/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/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.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.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.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/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.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/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/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 +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CsvPreviewDialog.js","names":[],"sources":["../../../../src/components/table/CsvPreview/CsvPreviewDialog.tsx"],"sourcesContent":["import { DialogBase } from '@/components/DialogBase'\nimport {\n BasicFileHandleUpload,\n FileUploadHandle,\n} from '@/components/file/upload/BasicFileHandleUpload'\nimport { ErrorBanner } from '@/components/index'\nimport CsvPreview from '@/components/table/CsvPreview/CsvPreview'\nimport CsvTableDescriptorForm, {\n CsvTableDescriptorFormHandle,\n} from '@/components/table/CsvTableDescriptorForm/CsvTableDescriptorForm'\nimport { RefreshTwoTone } from '@mui/icons-material'\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore'\nimport Accordion from '@mui/material/Accordion'\nimport AccordionDetails from '@mui/material/AccordionDetails'\nimport AccordionSummary from '@mui/material/AccordionSummary'\nimport Button from '@mui/material/Button'\nimport Stack from '@mui/material/Stack'\nimport Typography from '@mui/material/Typography'\nimport {\n ColumnModel,\n CsvTableDescriptor,\n UploadToTablePreviewResult,\n} from '@sage-bionetworks/synapse-client'\nimport { useCallback, useRef, useState } from 'react'\n\nenum CsvPreviewDialogStep {\n UPLOAD_CSV = 0,\n COLUMN_PREVIEW = 1,\n}\n\nexport type CsvPreviewDialogProps = {\n /** Whether the dialog is open */\n open: boolean\n /** Callback when the dialog is closed */\n onClose: () => void\n /** Callback when the user confirms the column models\n * @param dataFileHandleId - The file handle ID of the uploaded CSV\n * @param columnModels - The confirmed column models\n * */\n onConfirm: (\n dataFileHandleId: string,\n columnModels: ColumnModel[],\n csvTableDescriptor: CsvTableDescriptor,\n ) => void\n /** Whether the confirm action is pending */\n confirmIsPending?: boolean\n /** An optional error message to display */\n errorMessage?: string\n}\n\nexport default function CsvPreviewDialog(props: CsvPreviewDialogProps) {\n const { open, onClose, onConfirm, confirmIsPending, errorMessage } = props\n const [step, setStep] = useState(CsvPreviewDialogStep.UPLOAD_CSV)\n const [csvTableDescriptor, setCsvTableDescriptor] =\n useState<CsvTableDescriptor>({\n separator: ',',\n quoteCharacter: '\"',\n escapeCharacter: '\\\\',\n lineEnd: '\\n',\n isFirstLineHeader: true,\n })\n const [csvPreviewData, setCsvPreviewData] =\n useState<UploadToTablePreviewResult | null>(null)\n const [isLoadingPreview, setIsLoadingPreview] = useState(false)\n\n const [uploadedFileHandleId, setUploadedFileHandleId] = useState<\n string | null\n >(null)\n\n const onFileUploaded = useCallback((fileHandleId: string) => {\n setUploadedFileHandleId(fileHandleId)\n setStep(CsvPreviewDialogStep.COLUMN_PREVIEW)\n }, [])\n\n const uploadRef = useRef<FileUploadHandle | null>(null)\n const csvDescriptorFormRef = useRef<CsvTableDescriptorFormHandle | null>(null)\n\n const uploadStepContent = (\n <BasicFileHandleUpload\n ref={uploadRef}\n allowMultipleUpload={false}\n onFileUploadComplete={fileHandleId => {\n onFileUploaded(fileHandleId)\n }}\n disableDragAndDrop={true}\n />\n )\n\n const previewStepContent = (\n <Stack spacing={2}>\n {uploadedFileHandleId && (\n <CsvPreview\n fileHandleId={uploadedFileHandleId}\n csvTableDescriptor={csvTableDescriptor}\n onCsvPreviewDataChange={setCsvPreviewData}\n onIsLoadingChange={setIsLoadingPreview}\n />\n )}\n <Accordion>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n <Typography variant={'headline3'}>Show Options</Typography>\n </AccordionSummary>\n <AccordionDetails>\n <CsvTableDescriptorForm\n defaultValue={csvTableDescriptor}\n ref={csvDescriptorFormRef}\n />\n <Button\n variant={'outlined'}\n startIcon={<RefreshTwoTone />}\n sx={{ mt: 2 }}\n onClick={() => {\n // Get the state from the form and update local state, which will re-render the preview\n if (csvDescriptorFormRef.current) {\n setCsvTableDescriptor(\n csvDescriptorFormRef.current.getFormData(),\n )\n }\n }}\n >\n Refresh Preview\n </Button>\n </AccordionDetails>\n </Accordion>\n </Stack>\n )\n\n return (\n <DialogBase\n maxWidth={'lg'}\n title={'Upload CSV'}\n onCancel={onClose}\n open={open}\n content={\n <>\n {step === CsvPreviewDialogStep.UPLOAD_CSV && uploadStepContent}\n {step === CsvPreviewDialogStep.COLUMN_PREVIEW && previewStepContent}\n {errorMessage && <ErrorBanner error={errorMessage} />}\n </>\n }\n actions={\n <>\n <Button\n variant={'outlined'}\n disabled={isLoadingPreview}\n onClick={() => {\n onClose()\n }}\n >\n Cancel\n </Button>\n {step === CsvPreviewDialogStep.COLUMN_PREVIEW && (\n <Button\n disabled={isLoadingPreview}\n variant={'contained'}\n onClick={() => {\n onConfirm(\n uploadedFileHandleId!,\n csvPreviewData!.suggestedColumns!,\n csvTableDescriptor,\n )\n }}\n loading={confirmIsPending}\n >\n Confirm\n </Button>\n )}\n </>\n }\n />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAyBA,IAAK,IAAL,yBAAA,GAAA;QACE,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"CsvPreviewDialog.js","names":[],"sources":["../../../../src/components/table/CsvPreview/CsvPreviewDialog.tsx"],"sourcesContent":["import { DialogBase } from '@/components/DialogBase'\nimport {\n BasicFileHandleUpload,\n FileUploadHandle,\n} from '@/components/file/upload/BasicFileHandleUpload'\nimport { ErrorBanner } from '@/components/index'\nimport CsvPreview from '@/components/table/CsvPreview/CsvPreview'\nimport CsvTableDescriptorForm, {\n CsvTableDescriptorFormHandle,\n} from '@/components/table/CsvTableDescriptorForm/CsvTableDescriptorForm'\nimport { RefreshTwoTone } from '@mui/icons-material'\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore'\nimport Accordion from '@mui/material/Accordion'\nimport AccordionDetails from '@mui/material/AccordionDetails'\nimport AccordionSummary from '@mui/material/AccordionSummary'\nimport Button from '@mui/material/Button'\nimport Stack from '@mui/material/Stack'\nimport Typography from '@mui/material/Typography'\nimport {\n ColumnModel,\n CsvTableDescriptor,\n UploadToTablePreviewResult,\n} from '@sage-bionetworks/synapse-client'\nimport { useCallback, useRef, useState } from 'react'\n\nenum CsvPreviewDialogStep {\n UPLOAD_CSV = 0,\n COLUMN_PREVIEW = 1,\n}\n\nexport type CsvPreviewDialogProps = {\n /** Whether the dialog is open */\n open: boolean\n /** Callback when the dialog is closed */\n onClose: () => void\n /** Callback when the user confirms the column models\n * @param dataFileHandleId - The file handle ID of the uploaded CSV\n * @param columnModels - The confirmed column models\n * */\n onConfirm: (\n dataFileHandleId: string,\n columnModels: ColumnModel[],\n csvTableDescriptor: CsvTableDescriptor,\n ) => void\n /** Whether the confirm action is pending */\n confirmIsPending?: boolean\n /** An optional error message to display */\n errorMessage?: string\n}\n\nexport default function CsvPreviewDialog(props: CsvPreviewDialogProps) {\n const { open, onClose, onConfirm, confirmIsPending, errorMessage } = props\n const [step, setStep] = useState(CsvPreviewDialogStep.UPLOAD_CSV)\n const [csvTableDescriptor, setCsvTableDescriptor] =\n useState<CsvTableDescriptor>({\n separator: ',',\n quoteCharacter: '\"',\n escapeCharacter: '\\\\',\n lineEnd: '\\n',\n isFirstLineHeader: true,\n })\n const [csvPreviewData, setCsvPreviewData] =\n useState<UploadToTablePreviewResult | null>(null)\n const [isLoadingPreview, setIsLoadingPreview] = useState(false)\n\n const [uploadedFileHandleId, setUploadedFileHandleId] = useState<\n string | null\n >(null)\n\n const onFileUploaded = useCallback((fileHandleId: string) => {\n setUploadedFileHandleId(fileHandleId)\n setStep(CsvPreviewDialogStep.COLUMN_PREVIEW)\n }, [])\n\n const uploadRef = useRef<FileUploadHandle | null>(null)\n const csvDescriptorFormRef = useRef<CsvTableDescriptorFormHandle | null>(null)\n\n const uploadStepContent = (\n <BasicFileHandleUpload\n ref={uploadRef}\n allowMultipleUpload={false}\n onFileUploadComplete={fileHandleId => {\n onFileUploaded(fileHandleId)\n }}\n disableDragAndDrop={true}\n />\n )\n\n const previewStepContent = (\n <Stack spacing={2}>\n {uploadedFileHandleId && (\n <CsvPreview\n fileHandleId={uploadedFileHandleId}\n csvTableDescriptor={csvTableDescriptor}\n onCsvPreviewDataChange={setCsvPreviewData}\n onIsLoadingChange={setIsLoadingPreview}\n />\n )}\n <Accordion>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n <Typography variant={'headline3'}>Show Options</Typography>\n </AccordionSummary>\n <AccordionDetails>\n <CsvTableDescriptorForm\n defaultValue={csvTableDescriptor}\n ref={csvDescriptorFormRef}\n />\n <Button\n variant={'outlined'}\n startIcon={<RefreshTwoTone />}\n sx={{ mt: 2 }}\n onClick={() => {\n // Get the state from the form and update local state, which will re-render the preview\n if (csvDescriptorFormRef.current) {\n setCsvTableDescriptor(\n csvDescriptorFormRef.current.getFormData(),\n )\n }\n }}\n >\n Refresh Preview\n </Button>\n </AccordionDetails>\n </Accordion>\n </Stack>\n )\n\n return (\n <DialogBase\n maxWidth={'lg'}\n title={'Upload CSV'}\n onCancel={onClose}\n open={open}\n content={\n <>\n {step === CsvPreviewDialogStep.UPLOAD_CSV && uploadStepContent}\n {step === CsvPreviewDialogStep.COLUMN_PREVIEW && previewStepContent}\n {errorMessage && <ErrorBanner error={errorMessage} />}\n </>\n }\n actions={\n <>\n <Button\n variant={'outlined'}\n disabled={isLoadingPreview}\n onClick={() => {\n onClose()\n }}\n >\n Cancel\n </Button>\n {step === CsvPreviewDialogStep.COLUMN_PREVIEW && (\n <Button\n disabled={isLoadingPreview}\n variant={'contained'}\n onClick={() => {\n onConfirm(\n uploadedFileHandleId!,\n csvPreviewData!.suggestedColumns!,\n csvTableDescriptor,\n )\n }}\n loading={confirmIsPending}\n >\n Confirm\n </Button>\n )}\n </>\n }\n />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAyBA,IAAK,IAAL,yBAAA,GAAA;QACE,EAAA,EAAA,aAAa,KAAA,cACb,EAAA,EAAA,iBAAiB,KAAA;EAFd,KAAA,EAAA,CAGJ;AAsBD,SAAwB,EAAiB,GAA8B;CACrE,IAAM,EAAE,SAAM,YAAS,cAAW,qBAAkB,oBAAiB,GAC/D,CAAC,GAAM,KAAW,EAAS,EAAqB,WAAW,EAC3D,CAAC,GAAoB,KACzB,EAA6B;EAC3B,WAAW;EACX,gBAAgB;EAChB,iBAAiB;EACjB,SAAS;EACT,mBAAmB;EACpB,CAAC,EACE,CAAC,GAAgB,KACrB,EAA4C,KAAK,EAC7C,CAAC,GAAkB,KAAuB,EAAS,GAAM,EAEzD,CAAC,GAAsB,KAA2B,EAEtD,KAAK,EAED,IAAiB,GAAa,MAAyB;AAE3D,EADA,EAAwB,EAAa,EACrC,EAAQ,EAAqB,eAAe;IAC3C,EAAE,CAAC,EAEA,IAAY,EAAgC,KAAK,EACjD,IAAuB,EAA4C,KAAK,EAExE,IACJ,kBAAC,GAAD;EACE,KAAK;EACL,qBAAqB;EACrB,uBAAsB,MAAgB;AACpC,KAAe,EAAa;;EAE9B,oBAAoB;EACpB,CAAA,EAGE,IACJ,kBAAC,GAAD;EAAO,SAAS;YAAhB,CACG,KACC,kBAAC,GAAD;GACE,cAAc;GACM;GACpB,wBAAwB;GACxB,mBAAmB;GACnB,CAAA,EAEJ,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GAAkB,YAAY,kBAAC,GAAD,EAAkB,CAAA;aAC9C,kBAAC,GAAD;IAAY,SAAS;cAAa;IAAyB,CAAA;GAC1C,CAAA,EACnB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GACE,cAAc;GACd,KAAK;GACL,CAAA,EACF,kBAAC,GAAD;GACE,SAAS;GACT,WAAW,kBAAC,GAAD,EAAkB,CAAA;GAC7B,IAAI,EAAE,IAAI,GAAG;GACb,eAAe;AAEb,IAAI,EAAqB,WACvB,EACE,EAAqB,QAAQ,aAAa,CAC3C;;aAGN;GAEQ,CAAA,CACQ,EAAA,CAAA,CACT,EAAA,CAAA,CACN;;AAGV,QACE,kBAAC,GAAD;EACE,UAAU;EACV,OAAO;EACP,UAAU;EACJ;EACN,SACE,kBAAA,GAAA,EAAA,UAAA;GACG,MAAS,EAAqB,cAAc;GAC5C,MAAS,EAAqB,kBAAkB;GAChD,KAAgB,kBAAC,GAAD,EAAa,OAAO,GAAgB,CAAA;GACpD,EAAA,CAAA;EAEL,SACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;GACE,SAAS;GACT,UAAU;GACV,eAAe;AACb,OAAS;;aAEZ;GAEQ,CAAA,EACR,MAAS,EAAqB,kBAC7B,kBAAC,GAAD;GACE,UAAU;GACV,SAAS;GACT,eAAe;AACb,MACE,GACA,EAAgB,kBAChB,EACD;;GAEH,SAAS;aACV;GAEQ,CAAA,CAEV,EAAA,CAAA;EAEL,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TrashCanList.js","names":[],"sources":["../../../src/components/trash/TrashCanList.tsx"],"sourcesContent":["import {\n useGetItemsInTrashCanInfinite,\n usePurgeEntities,\n useRestoreEntities,\n} from '@/synapse-queries/trash/useTrashCan'\nimport { formatDate } from '@/utils/functions/DateFormatter'\nimport { entityTypeToFriendlyName } from '@/utils/functions/EntityTypeUtils'\nimport { Alert, Box, Button, Typography } from '@mui/material'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport { TrashedEntity } from '@sage-bionetworks/synapse-types'\nimport {\n ColumnDef,\n createColumnHelper,\n getCoreRowModel,\n RowSelectionState,\n Table,\n useReactTable,\n} from '@tanstack/react-table'\nimport dayjs from 'dayjs'\nimport { useMemo, useState } from 'react'\nimport {\n CheckBoxCell,\n CheckBoxHeader,\n} from '../EntityHeaderTable/EntityHeaderTableCellRenderers'\nimport { EntityLink } from '../EntityLink'\nimport { BlockingLoader, SynapseSpinner } from '../LoadingScreen/LoadingScreen'\nimport WarningDialog from '../SynapseForm/WarningDialog'\nimport StyledTanStackTable from '../TanStackTable/StyledTanStackTable'\n\n/**\n * Convert an array of Promise results to an array of errors\n */\nfunction toSynapseClientErrorList(\n results: PromiseSettledResult<void>[],\n): SynapseClientError[] {\n return results\n .filter(\n (result): result is PromiseRejectedResult => result.status === 'rejected',\n )\n .map(result => result.reason as SynapseClientError)\n}\n\nconst columnHelper = createColumnHelper<TrashedEntity>()\n\nfunction getTrashCanColumns(\n onRestore: (entityId: string) => void,\n): ColumnDef<TrashedEntity, any>[] {\n return [\n {\n id: 'select',\n header: CheckBoxHeader,\n cell: CheckBoxCell,\n maxSize: 50,\n },\n columnHelper.accessor('entityId', {\n header: 'ID',\n }),\n columnHelper.accessor('entityName', {\n header: 'Name',\n }),\n columnHelper.accessor('entityType', {\n header: 'Entity Type',\n cell: info => entityTypeToFriendlyName(info.getValue()),\n }),\n columnHelper.accessor('originalParentId', {\n header: 'Location',\n cell: info => (\n <>\n {info.getValue() && <EntityLink entity={info.getValue()} />} (\n {info.getValue()})\n </>\n ),\n size: 200,\n }),\n columnHelper.accessor('deletedOn', {\n header: 'Deleted On',\n cell: info => formatDate(dayjs(info.getValue())),\n }),\n {\n id: 'restoreButton',\n header: '',\n cell: ({ row }) => (\n <Button\n size=\"small\"\n variant=\"outlined\"\n onClick={() => {\n onRestore(row.original.entityId)\n row.toggleSelected(false)\n }}\n >\n Restore\n </Button>\n ),\n maxSize: 100,\n },\n ]\n}\n\nexport function TrashCanList() {\n const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false)\n const [errors, setErrors] = useState<SynapseClientError[]>([])\n const [rowSelection, setRowSelection] = useState<RowSelectionState>({})\n\n /**\n * When a mutation operation settles, update the list of errors and clear the selected set\n */\n function onMutateSettled(\n results?: PromiseSettledResult<void>[],\n error?: SynapseClientError | null,\n ) {\n if (results) {\n setErrors(toSynapseClientErrorList(results))\n } else if (error) {\n setErrors([error])\n }\n setRowSelection({})\n }\n\n const { mutate: restore, isPending: isPendingRestore } = useRestoreEntities({\n onSettled: onMutateSettled,\n })\n const { mutate: purge, isPending: isPendingPurge } = usePurgeEntities({\n onSettled: onMutateSettled,\n })\n\n const isMutating = isPendingRestore || isPendingPurge\n\n const { data, isLoading, hasNextPage, fetchNextPage, isFetchingNextPage } =\n useGetItemsInTrashCanInfinite({\n throwOnError: true,\n })\n const items = useMemo(\n () => data?.pages.flatMap(page => page.results) ?? [],\n [data],\n )\n\n const columns = useMemo<ColumnDef<TrashedEntity, any>[]>(\n () =>\n getTrashCanColumns(entityId => {\n restore(entityId)\n }),\n [restore],\n )\n\n const table: Table<TrashedEntity> = useReactTable<TrashedEntity>({\n data: items,\n columns,\n state: {\n rowSelection: rowSelection,\n },\n getRowId: row => row.entityId,\n enableRowSelection: true,\n onRowSelectionChange: setRowSelection,\n getCoreRowModel: getCoreRowModel(),\n columnResizeMode: 'onChange',\n })\n\n const selectedEntityIds = table\n .getSelectedRowModel()\n .rows.map(row => row.original.entityId)\n\n return (\n <div>\n <BlockingLoader\n show={isMutating}\n headlineText={isPendingPurge ? 'Deleting...' : 'Restoring...'}\n />\n <Typography variant=\"body1\">\n The trash can contains items that were recently deleted. You can recover\n deleted items in the trash can by clicking "Restore". Items\n will remain in the trash can for 30 days before being automatically\n purged.\n </Typography>\n <WarningDialog\n title=\"Delete selected items from your Trash?\"\n content={\n <Typography variant=\"body1\">\n You can't undo this action.\n </Typography>\n }\n confirmButtonText=\"Delete\"\n confirmButtonColor=\"error\"\n onConfirm={() => {\n purge(selectedEntityIds)\n setShowDeleteConfirmation(false)\n }}\n onCancel={() => {\n setShowDeleteConfirmation(false)\n }}\n open={showDeleteConfirmation}\n />\n {isLoading && <SynapseSpinner />}\n {!isLoading && items.length === 0 && (\n <Alert severity={'info'} sx={{ my: 2 }}>\n <Typography variant=\"body1\">\n Your trash can is currently empty.\n </Typography>\n </Alert>\n )}\n {!isLoading && items.length > 0 && (\n <>\n <StyledTanStackTable\n table={table}\n styledTableContainerProps={{ sx: { my: 4 } }}\n />\n {errors.length > 0 && (\n <Alert severity={'error'} sx={{ mb: 1 }}>\n The following errors were encountered:\n <ul style={{ marginBottom: 0 }}>\n {errors.map(error => (\n <li key={error.message}>{error.message}</li>\n ))}\n </ul>\n </Alert>\n )}\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'flex-end',\n gap: 2,\n }}\n >\n {hasNextPage && (\n <Button\n variant=\"contained\"\n disabled={isFetchingNextPage}\n onClick={() => {\n fetchNextPage()\n }}\n >\n Load More\n </Button>\n )}\n <div style={{ margin: 'auto' }} />\n <Button\n variant=\"contained\"\n color=\"error\"\n disabled={selectedEntityIds.length === 0}\n onClick={() => {\n setShowDeleteConfirmation(true)\n }}\n >\n Delete Selected\n </Button>\n <Button\n variant=\"outlined\"\n disabled={selectedEntityIds.length === 0}\n onClick={() => {\n restore(selectedEntityIds)\n }}\n >\n Restore Selected\n </Button>\n </Box>\n </>\n )}\n </div>\n )\n}\n\nexport default TrashCanList\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TrashCanList.js","names":[],"sources":["../../../src/components/trash/TrashCanList.tsx"],"sourcesContent":["import {\n useGetItemsInTrashCanInfinite,\n usePurgeEntities,\n useRestoreEntities,\n} from '@/synapse-queries/trash/useTrashCan'\nimport { formatDate } from '@/utils/functions/DateFormatter'\nimport { entityTypeToFriendlyName } from '@/utils/functions/EntityTypeUtils'\nimport { Alert, Box, Button, Typography } from '@mui/material'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport { TrashedEntity } from '@sage-bionetworks/synapse-types'\nimport {\n ColumnDef,\n createColumnHelper,\n getCoreRowModel,\n RowSelectionState,\n Table,\n useReactTable,\n} from '@tanstack/react-table'\nimport dayjs from 'dayjs'\nimport { useMemo, useState } from 'react'\nimport {\n CheckBoxCell,\n CheckBoxHeader,\n} from '../EntityHeaderTable/EntityHeaderTableCellRenderers'\nimport { EntityLink } from '../EntityLink'\nimport { BlockingLoader, SynapseSpinner } from '../LoadingScreen/LoadingScreen'\nimport WarningDialog from '../SynapseForm/WarningDialog'\nimport StyledTanStackTable from '../TanStackTable/StyledTanStackTable'\n\n/**\n * Convert an array of Promise results to an array of errors\n */\nfunction toSynapseClientErrorList(\n results: PromiseSettledResult<void>[],\n): SynapseClientError[] {\n return results\n .filter(\n (result): result is PromiseRejectedResult => result.status === 'rejected',\n )\n .map(result => result.reason as SynapseClientError)\n}\n\nconst columnHelper = createColumnHelper<TrashedEntity>()\n\nfunction getTrashCanColumns(\n onRestore: (entityId: string) => void,\n): ColumnDef<TrashedEntity, any>[] {\n return [\n {\n id: 'select',\n header: CheckBoxHeader,\n cell: CheckBoxCell,\n maxSize: 50,\n },\n columnHelper.accessor('entityId', {\n header: 'ID',\n }),\n columnHelper.accessor('entityName', {\n header: 'Name',\n }),\n columnHelper.accessor('entityType', {\n header: 'Entity Type',\n cell: info => entityTypeToFriendlyName(info.getValue()),\n }),\n columnHelper.accessor('originalParentId', {\n header: 'Location',\n cell: info => (\n <>\n {info.getValue() && <EntityLink entity={info.getValue()} />} (\n {info.getValue()})\n </>\n ),\n size: 200,\n }),\n columnHelper.accessor('deletedOn', {\n header: 'Deleted On',\n cell: info => formatDate(dayjs(info.getValue())),\n }),\n {\n id: 'restoreButton',\n header: '',\n cell: ({ row }) => (\n <Button\n size=\"small\"\n variant=\"outlined\"\n onClick={() => {\n onRestore(row.original.entityId)\n row.toggleSelected(false)\n }}\n >\n Restore\n </Button>\n ),\n maxSize: 100,\n },\n ]\n}\n\nexport function TrashCanList() {\n const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false)\n const [errors, setErrors] = useState<SynapseClientError[]>([])\n const [rowSelection, setRowSelection] = useState<RowSelectionState>({})\n\n /**\n * When a mutation operation settles, update the list of errors and clear the selected set\n */\n function onMutateSettled(\n results?: PromiseSettledResult<void>[],\n error?: SynapseClientError | null,\n ) {\n if (results) {\n setErrors(toSynapseClientErrorList(results))\n } else if (error) {\n setErrors([error])\n }\n setRowSelection({})\n }\n\n const { mutate: restore, isPending: isPendingRestore } = useRestoreEntities({\n onSettled: onMutateSettled,\n })\n const { mutate: purge, isPending: isPendingPurge } = usePurgeEntities({\n onSettled: onMutateSettled,\n })\n\n const isMutating = isPendingRestore || isPendingPurge\n\n const { data, isLoading, hasNextPage, fetchNextPage, isFetchingNextPage } =\n useGetItemsInTrashCanInfinite({\n throwOnError: true,\n })\n const items = useMemo(\n () => data?.pages.flatMap(page => page.results) ?? [],\n [data],\n )\n\n const columns = useMemo<ColumnDef<TrashedEntity, any>[]>(\n () =>\n getTrashCanColumns(entityId => {\n restore(entityId)\n }),\n [restore],\n )\n\n const table: Table<TrashedEntity> = useReactTable<TrashedEntity>({\n data: items,\n columns,\n state: {\n rowSelection: rowSelection,\n },\n getRowId: row => row.entityId,\n enableRowSelection: true,\n onRowSelectionChange: setRowSelection,\n getCoreRowModel: getCoreRowModel(),\n columnResizeMode: 'onChange',\n })\n\n const selectedEntityIds = table\n .getSelectedRowModel()\n .rows.map(row => row.original.entityId)\n\n return (\n <div>\n <BlockingLoader\n show={isMutating}\n headlineText={isPendingPurge ? 'Deleting...' : 'Restoring...'}\n />\n <Typography variant=\"body1\">\n The trash can contains items that were recently deleted. You can recover\n deleted items in the trash can by clicking "Restore". Items\n will remain in the trash can for 30 days before being automatically\n purged.\n </Typography>\n <WarningDialog\n title=\"Delete selected items from your Trash?\"\n content={\n <Typography variant=\"body1\">\n You can't undo this action.\n </Typography>\n }\n confirmButtonText=\"Delete\"\n confirmButtonColor=\"error\"\n onConfirm={() => {\n purge(selectedEntityIds)\n setShowDeleteConfirmation(false)\n }}\n onCancel={() => {\n setShowDeleteConfirmation(false)\n }}\n open={showDeleteConfirmation}\n />\n {isLoading && <SynapseSpinner />}\n {!isLoading && items.length === 0 && (\n <Alert severity={'info'} sx={{ my: 2 }}>\n <Typography variant=\"body1\">\n Your trash can is currently empty.\n </Typography>\n </Alert>\n )}\n {!isLoading && items.length > 0 && (\n <>\n <StyledTanStackTable\n table={table}\n styledTableContainerProps={{ sx: { my: 4 } }}\n />\n {errors.length > 0 && (\n <Alert severity={'error'} sx={{ mb: 1 }}>\n The following errors were encountered:\n <ul style={{ marginBottom: 0 }}>\n {errors.map(error => (\n <li key={error.message}>{error.message}</li>\n ))}\n </ul>\n </Alert>\n )}\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'flex-end',\n gap: 2,\n }}\n >\n {hasNextPage && (\n <Button\n variant=\"contained\"\n disabled={isFetchingNextPage}\n onClick={() => {\n fetchNextPage()\n }}\n >\n Load More\n </Button>\n )}\n <div style={{ margin: 'auto' }} />\n <Button\n variant=\"contained\"\n color=\"error\"\n disabled={selectedEntityIds.length === 0}\n onClick={() => {\n setShowDeleteConfirmation(true)\n }}\n >\n Delete Selected\n </Button>\n <Button\n variant=\"outlined\"\n disabled={selectedEntityIds.length === 0}\n onClick={() => {\n restore(selectedEntityIds)\n }}\n >\n Restore Selected\n </Button>\n </Box>\n </>\n )}\n </div>\n )\n}\n\nexport default TrashCanList\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileHandleLink.js","names":[],"sources":["../../../src/components/widgets/FileHandleLink.tsx"],"sourcesContent":["import React from 'react'\nimport SynapseClient from '@/synapse-client'\nimport { SynapseConstants } from '@/utils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport {\n BatchFileRequest,\n BatchFileResult,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\nimport { useEffect, useState } from 'react'\nimport IconSvg from '../IconSvg/IconSvg'\n\ntype FileHandleLinkProps = {\n fileHandleAssociation: FileHandleAssociation\n redirect?: boolean\n showDownloadIcon: boolean\n displayValue?: string\n}\nexport const FileHandleLink = (props: FileHandleLinkProps): React.ReactNode => {\n const {\n fileHandleAssociation,\n showDownloadIcon,\n redirect = false,\n displayValue,\n } = props\n const { accessToken } = useSynapseContext()\n\n const [batchFileResult, setBatchFileResult] = useState<\n BatchFileResult | undefined\n >()\n\n useEffect(() => {\n if (displayValue === undefined) {\n const getFiles = async () => {\n const batchFileRequest: BatchFileRequest = {\n requestedFiles: [fileHandleAssociation],\n includeFileHandles: true,\n includePreSignedURLs: false,\n includePreviewPreSignedURLs: false,\n }\n setBatchFileResult(\n await SynapseClient.getFiles(batchFileRequest, accessToken),\n )\n }\n getFiles()\n }\n }, [accessToken, displayValue, fileHandleAssociation])\n\n let fileName: string | undefined = undefined\n if (batchFileResult) {\n fileName = batchFileResult.requestedFiles[0].fileHandle?.fileName\n }\n\n return (\n <button\n onClick={() => {\n if (accessToken && fileHandleAssociation) {\n SynapseClient.getActualFileHandleByIdURL(\n fileHandleAssociation.fileHandleId,\n accessToken,\n fileHandleAssociation.associateObjectType,\n fileHandleAssociation.associateObjectId,\n redirect,\n )\n .then(url => {\n window.open(url, '_blank')\n })\n .catch(err => {\n console.error('Error on retrieving file handle url ', err)\n })\n }\n }}\n className={`SRC-primary-text-color ${SynapseConstants.SRC_SIGN_IN_CLASS}`}\n type=\"button\"\n style={{ padding: 0 }}\n >\n {displayValue ?? fileName ?? fileHandleAssociation.fileHandleId}\n {showDownloadIcon && <IconSvg icon=\"download\" />}\n </button>\n )\n}\n"],"mappings":";;;;;;;;AAkBA,IAAa,KAAkB,MAAgD;CAC7E,IAAM,EACJ,0BACA,qBACA,cAAW,IACX,oBACE,GACE,EAAE,mBAAgB,GAAmB,EAErC,CAAC,GAAiB,KAAsB,GAE3C;AAEH,SAAgB;AACd,EAAI,MAAiB,KAAA,
|
|
1
|
+
{"version":3,"file":"FileHandleLink.js","names":[],"sources":["../../../src/components/widgets/FileHandleLink.tsx"],"sourcesContent":["import React from 'react'\nimport SynapseClient from '@/synapse-client'\nimport { SynapseConstants } from '@/utils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport {\n BatchFileRequest,\n BatchFileResult,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\nimport { useEffect, useState } from 'react'\nimport IconSvg from '../IconSvg/IconSvg'\n\ntype FileHandleLinkProps = {\n fileHandleAssociation: FileHandleAssociation\n redirect?: boolean\n showDownloadIcon: boolean\n displayValue?: string\n}\nexport const FileHandleLink = (props: FileHandleLinkProps): React.ReactNode => {\n const {\n fileHandleAssociation,\n showDownloadIcon,\n redirect = false,\n displayValue,\n } = props\n const { accessToken } = useSynapseContext()\n\n const [batchFileResult, setBatchFileResult] = useState<\n BatchFileResult | undefined\n >()\n\n useEffect(() => {\n if (displayValue === undefined) {\n const getFiles = async () => {\n const batchFileRequest: BatchFileRequest = {\n requestedFiles: [fileHandleAssociation],\n includeFileHandles: true,\n includePreSignedURLs: false,\n includePreviewPreSignedURLs: false,\n }\n setBatchFileResult(\n await SynapseClient.getFiles(batchFileRequest, accessToken),\n )\n }\n getFiles()\n }\n }, [accessToken, displayValue, fileHandleAssociation])\n\n let fileName: string | undefined = undefined\n if (batchFileResult) {\n fileName = batchFileResult.requestedFiles[0].fileHandle?.fileName\n }\n\n return (\n <button\n onClick={() => {\n if (accessToken && fileHandleAssociation) {\n SynapseClient.getActualFileHandleByIdURL(\n fileHandleAssociation.fileHandleId,\n accessToken,\n fileHandleAssociation.associateObjectType,\n fileHandleAssociation.associateObjectId,\n redirect,\n )\n .then(url => {\n window.open(url, '_blank')\n })\n .catch(err => {\n console.error('Error on retrieving file handle url ', err)\n })\n }\n }}\n className={`SRC-primary-text-color ${SynapseConstants.SRC_SIGN_IN_CLASS}`}\n type=\"button\"\n style={{ padding: 0 }}\n >\n {displayValue ?? fileName ?? fileHandleAssociation.fileHandleId}\n {showDownloadIcon && <IconSvg icon=\"download\" />}\n </button>\n )\n}\n"],"mappings":";;;;;;;;AAkBA,IAAa,KAAkB,MAAgD;CAC7E,IAAM,EACJ,0BACA,qBACA,cAAW,IACX,oBACE,GACE,EAAE,mBAAgB,GAAmB,EAErC,CAAC,GAAiB,KAAsB,GAE3C;AAEH,SAAgB;AACd,EAAI,MAAiB,KAAA,MAYnB,YAX6B;GAC3B,IAAM,IAAqC;IACzC,gBAAgB,CAAC,EAAsB;IACvC,oBAAoB;IACpB,sBAAsB;IACtB,6BAA6B;IAC9B;AACD,KACE,MAAM,EAAc,SAAS,GAAkB,EAAY,CAC5D;MAEO;IAEX;EAAC;EAAa;EAAc;EAAsB,CAAC;CAEtD,IAAI;AAKJ,QAJI,MACF,IAAW,EAAgB,eAAe,GAAG,YAAY,WAIzD,kBAAC,UAAD;EACE,eAAe;AACb,GAAI,KAAe,KACjB,EAAc,2BACZ,EAAsB,cACtB,GACA,EAAsB,qBACtB,EAAsB,mBACtB,EACD,CACE,MAAK,MAAO;AACX,WAAO,KAAK,GAAK,SAAS;KAC1B,CACD,OAAM,MAAO;AACZ,YAAQ,MAAM,wCAAwC,EAAI;KAC1D;;EAGR,WAAW,0BAA0B;EACrC,MAAK;EACL,OAAO,EAAE,SAAS,GAAG;YApBvB,CAsBG,KAAgB,KAAY,EAAsB,cAClD,KAAoB,kBAAC,GAAD,EAAS,MAAK,YAAa,CAAA,CACzC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RangeSlider.js","names":[],"sources":["../../../../src/components/widgets/RangeSlider/RangeSlider.tsx"],"sourcesContent":["import { RangeValues } from '../Range'\nimport { PropsWithChildren, useState } from 'react'\nimport {\n Box,\n Button,\n Slider,\n SliderValueLabelProps,\n Tooltip,\n Typography,\n} from '@mui/material'\n\nexport type RangeSliderProps = PropsWithChildren<{\n domain: string[]\n initialValues: RangeValues\n step: number\n onChange?: (values: RangeValues) => void\n onApplyClicked?: (values: RangeValues) => void\n}>\n\nfunction ValueLabelComponent(props: SliderValueLabelProps) {\n const { children, value } = props\n\n return (\n <Tooltip enterTouchDelay={0} placement=\"top\" title={value}>\n {children}\n </Tooltip>\n )\n}\n\nfunction getInitialValues(initialValues: RangeValues, domain: string[]) {\n const result = [\n initialValues.min ? Number(initialValues.min) : Number(domain[0]),\n initialValues.max ? Number(initialValues.max) : Number(domain[1]),\n ]\n return result\n}\n\nfunction RangeSlider(props: RangeSliderProps) {\n const { onApplyClicked, onChange, step } = props\n const stringArrToNumArr = (inputArr: string[]) =>\n inputArr.map(value => Number(value))\n\n const [values, setValues] = useState<number[]>(() =>\n getInitialValues(props.initialValues, props.domain),\n )\n\n const numDomain = stringArrToNumArr(props.domain)\n\n const handleSliderChange = (values: readonly number[]) => {\n setValues([...values])\n if (onChange) {\n onChange({ min: values[0], max: values[1] })\n }\n }\n\n return (\n <Box sx={{ ml: 1 }}>\n <Typography variant=\"smallText1\">\n {values[0]} - {values[1]}\n </Typography>\n <Box\n sx={{\n display: 'flex',\n gap: 3,\n ml: 1,\n }}\n >\n <Slider\n marks={[\n { value: numDomain[0], label: props.domain[0] },\n { value: numDomain[1], label: props.domain[1] },\n ]}\n min={numDomain[0]}\n max={numDomain[1]}\n value={values}\n onChange={(_, newValues) => handleSliderChange(newValues)}\n step={step}\n valueLabelDisplay=\"auto\"\n slots={{\n valueLabel: ValueLabelComponent,\n }}\n />\n {onApplyClicked && (\n <Box>\n <Button\n size=\"small\"\n variant=\"outlined\"\n onClick={() => onApplyClicked({ min: values[0], max: values[1] })}\n sx={{ fontSize: 16 }}\n >\n Apply\n </Button>\n </Box>\n )}\n </Box>\n </Box>\n )\n}\n\nexport default RangeSlider\n"],"mappings":";;;;AAmBA,SAAS,EAAoB,GAA8B;CACzD,IAAM,EAAE,aAAU,aAAU;AAE5B,QACE,kBAAC,GAAD;EAAS,iBAAiB;EAAG,WAAU;EAAM,OAAO;EACjD;EACO,CAAA;;AAId,SAAS,EAAiB,GAA4B,GAAkB;AAKtE,
|
|
1
|
+
{"version":3,"file":"RangeSlider.js","names":[],"sources":["../../../../src/components/widgets/RangeSlider/RangeSlider.tsx"],"sourcesContent":["import { RangeValues } from '../Range'\nimport { PropsWithChildren, useState } from 'react'\nimport {\n Box,\n Button,\n Slider,\n SliderValueLabelProps,\n Tooltip,\n Typography,\n} from '@mui/material'\n\nexport type RangeSliderProps = PropsWithChildren<{\n domain: string[]\n initialValues: RangeValues\n step: number\n onChange?: (values: RangeValues) => void\n onApplyClicked?: (values: RangeValues) => void\n}>\n\nfunction ValueLabelComponent(props: SliderValueLabelProps) {\n const { children, value } = props\n\n return (\n <Tooltip enterTouchDelay={0} placement=\"top\" title={value}>\n {children}\n </Tooltip>\n )\n}\n\nfunction getInitialValues(initialValues: RangeValues, domain: string[]) {\n const result = [\n initialValues.min ? Number(initialValues.min) : Number(domain[0]),\n initialValues.max ? Number(initialValues.max) : Number(domain[1]),\n ]\n return result\n}\n\nfunction RangeSlider(props: RangeSliderProps) {\n const { onApplyClicked, onChange, step } = props\n const stringArrToNumArr = (inputArr: string[]) =>\n inputArr.map(value => Number(value))\n\n const [values, setValues] = useState<number[]>(() =>\n getInitialValues(props.initialValues, props.domain),\n )\n\n const numDomain = stringArrToNumArr(props.domain)\n\n const handleSliderChange = (values: readonly number[]) => {\n setValues([...values])\n if (onChange) {\n onChange({ min: values[0], max: values[1] })\n }\n }\n\n return (\n <Box sx={{ ml: 1 }}>\n <Typography variant=\"smallText1\">\n {values[0]} - {values[1]}\n </Typography>\n <Box\n sx={{\n display: 'flex',\n gap: 3,\n ml: 1,\n }}\n >\n <Slider\n marks={[\n { value: numDomain[0], label: props.domain[0] },\n { value: numDomain[1], label: props.domain[1] },\n ]}\n min={numDomain[0]}\n max={numDomain[1]}\n value={values}\n onChange={(_, newValues) => handleSliderChange(newValues)}\n step={step}\n valueLabelDisplay=\"auto\"\n slots={{\n valueLabel: ValueLabelComponent,\n }}\n />\n {onApplyClicked && (\n <Box>\n <Button\n size=\"small\"\n variant=\"outlined\"\n onClick={() => onApplyClicked({ min: values[0], max: values[1] })}\n sx={{ fontSize: 16 }}\n >\n Apply\n </Button>\n </Box>\n )}\n </Box>\n </Box>\n )\n}\n\nexport default RangeSlider\n"],"mappings":";;;;AAmBA,SAAS,EAAoB,GAA8B;CACzD,IAAM,EAAE,aAAU,aAAU;AAE5B,QACE,kBAAC,GAAD;EAAS,iBAAiB;EAAG,WAAU;EAAM,OAAO;EACjD;EACO,CAAA;;AAId,SAAS,EAAiB,GAA4B,GAAkB;AAKtE,QAAO,CAHL,EAAc,MAAM,OAAO,EAAc,IAAI,GAAG,OAAO,EAAO,GAAG,EACjE,EAAc,MAAM,OAAO,EAAc,IAAI,GAAG,OAAO,EAAO,GAAG,CAE5D;;AAGT,SAAS,EAAY,GAAyB;CAC5C,IAAM,EAAE,mBAAgB,aAAU,YAAS,GACrC,KAAqB,MACzB,EAAS,KAAI,MAAS,OAAO,EAAM,CAAC,EAEhC,CAAC,GAAQ,KAAa,QAC1B,EAAiB,EAAM,eAAe,EAAM,OAAO,CACpD,EAEK,IAAY,EAAkB,EAAM,OAAO,EAE3C,KAAsB,MAA8B;AAExD,EADA,EAAU,CAAC,GAAG,EAAO,CAAC,EAClB,KACF,EAAS;GAAE,KAAK,EAAO;GAAI,KAAK,EAAO;GAAI,CAAC;;AAIhD,QACE,kBAAC,GAAD;EAAK,IAAI,EAAE,IAAI,GAAG;YAAlB,CACE,kBAAC,GAAD;GAAY,SAAQ;aAApB;IACG,EAAO;IAAG;IAAI,EAAO;IACX;MACb,kBAAC,GAAD;GACE,IAAI;IACF,SAAS;IACT,KAAK;IACL,IAAI;IACL;aALH,CAOE,kBAAC,GAAD;IACE,OAAO,CACL;KAAE,OAAO,EAAU;KAAI,OAAO,EAAM,OAAO;KAAI,EAC/C;KAAE,OAAO,EAAU;KAAI,OAAO,EAAM,OAAO;KAAI,CAChD;IACD,KAAK,EAAU;IACf,KAAK,EAAU;IACf,OAAO;IACP,WAAW,GAAG,MAAc,EAAmB,EAAU;IACnD;IACN,mBAAkB;IAClB,OAAO,EACL,YAAY,GACb;IACD,CAAA,EACD,KACC,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IACE,MAAK;IACL,SAAQ;IACR,eAAe,EAAe;KAAE,KAAK,EAAO;KAAI,KAAK,EAAO;KAAI,CAAC;IACjE,IAAI,EAAE,UAAU,IAAI;cACrB;IAEQ,CAAA,EACL,CAAA,CAEJ;KACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SynapseVideo.js","names":[],"sources":["../../../src/components/widgets/SynapseVideo.tsx"],"sourcesContent":["import { getEntity, getFiles } from '@/synapse-client/SynapseClient'\nimport { SynapseConstants } from '@/utils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport {\n BatchFileRequest,\n BatchFileResult,\n FileEntity,\n FileHandleAssociateType,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\nimport { useEffect, useState } from 'react'\n\nexport type SynapseVideoProps = {\n params: {\n width?: string\n height?: string\n videoId?: string\n vimeoId?: string\n oggSynapseId?: string\n mp4SynapseId?: string\n webmSynapseId?: string\n vttSynapseId?: string\n }\n}\n\nexport default function SynapseVideo({ params }: SynapseVideoProps) {\n const { accessToken, isAuthenticated } = useSynapseContext()\n const [externalVideoUrl, setExternalVideoUrl] = useState<string>()\n const [synapseVideoPresignedUrl, setSynapseVideoPresignedUrl] =\n useState<string>()\n const [synapseVideoVttPresignedUrl, setSynapseVideoVttPresignedUrl] =\n useState<string>()\n\n const videoWidth = params.width ?? ''\n const videoHeight = params.height ?? ''\n useEffect(() => {\n const getVideo = async () => {\n if (params.videoId)\n setExternalVideoUrl(`https://www.youtube.com/embed/${params.videoId}`)\n else if (params.vimeoId)\n setExternalVideoUrl(`https://player.vimeo.com/video/${params.vimeoId}`)\n else {\n const videoKey =\n params.oggSynapseId || params.mp4SynapseId || params.webmSynapseId\n\n const videoEntity = await getEntity<FileEntity>(accessToken, videoKey!)\n const fileHandleAssociationList: FileHandleAssociation[] = [\n {\n associateObjectId: videoKey!,\n associateObjectType: FileHandleAssociateType.FileEntity,\n fileHandleId: videoEntity.dataFileHandleId,\n },\n ]\n\n let vttFileHandleId: string | undefined\n if (params.vttSynapseId) {\n const vttEntity = await getEntity<FileEntity>(\n accessToken,\n params.vttSynapseId,\n )\n vttFileHandleId = vttEntity.dataFileHandleId\n fileHandleAssociationList.push({\n associateObjectId: params.vttSynapseId,\n associateObjectType: FileHandleAssociateType.FileEntity,\n fileHandleId: vttEntity.dataFileHandleId,\n })\n }\n\n getSynapseFiles(\n fileHandleAssociationList,\n videoEntity.dataFileHandleId,\n vttFileHandleId,\n )\n }\n }\n\n const getSynapseFiles = (\n fileHandleAssociationList: FileHandleAssociation[],\n videoFileHandleId: string,\n vttFileHandleId?: string,\n ) => {\n const request: BatchFileRequest = {\n includeFileHandles: false,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n requestedFiles: fileHandleAssociationList,\n }\n\n getFiles(request, accessToken)\n .then((data: BatchFileResult) => {\n const videoFile = data.requestedFiles.find(\n el => el.fileHandleId === videoFileHandleId,\n )\n if (videoFile?.preSignedURL) {\n setSynapseVideoPresignedUrl(videoFile.preSignedURL)\n }\n\n if (vttFileHandleId) {\n const vttFile = data.requestedFiles.find(\n el => el.fileHandleId === vttFileHandleId,\n )\n if (vttFile?.preSignedURL) {\n setSynapseVideoVttPresignedUrl(vttFile.preSignedURL)\n }\n }\n })\n .catch(err => {\n console.error('Error on getting video ', err)\n })\n }\n getVideo()\n }, [externalVideoUrl, params, accessToken, videoHeight, videoWidth])\n\n if (!isAuthenticated && !externalVideoUrl) {\n // if not logged in, show login button\n return (\n <p>\n You will need to\n <button\n data-testid=\"video-login\"\n className={`${SynapseConstants.SRC_SIGN_IN_CLASS} sign-in-btn default\n `}\n >\n Sign in\n </button>\n in for access to that resource.\n </p>\n )\n }\n if (synapseVideoPresignedUrl) {\n return (\n <video\n controls\n width={videoWidth}\n height={videoHeight}\n data-testid=\"synapse-video-url\"\n crossOrigin=\"anonymous\"\n >\n <source src={synapseVideoPresignedUrl} />\n {synapseVideoVttPresignedUrl && (\n <track src={synapseVideoVttPresignedUrl} kind=\"subtitles\" default />\n )}\n It does not support the HTML5 Video element.\n </video>\n )\n } else if (externalVideoUrl) {\n return (\n <iframe\n title=\"video frame\"\n src={externalVideoUrl}\n width={videoWidth}\n height={videoHeight}\n ></iframe>\n )\n } else {\n return <></>\n }\n}\n"],"mappings":";;;;;;;;AAyBA,SAAwB,EAAa,EAAE,aAA6B;CAClE,IAAM,EAAE,gBAAa,uBAAoB,GAAmB,EACtD,CAAC,GAAkB,KAAuB,GAAkB,EAC5D,CAAC,GAA0B,KAC/B,GAAkB,EACd,CAAC,GAA6B,KAClC,GAAkB,EAEd,IAAa,EAAO,SAAS,IAC7B,IAAc,EAAO,UAAU;AAyHnC,QAxHF,QAAgB;EACd,IAAM,IAAW,YAAY;AAC3B,OAAI,EAAO,QACT,GAAoB,iCAAiC,EAAO,UAAU;YAC/D,EAAO,QACd,GAAoB,kCAAkC,EAAO,UAAU;QACpE;IACH,IAAM,IACJ,EAAO,gBAAgB,EAAO,gBAAgB,EAAO,eAEjD,IAAc,MAAM,EAAsB,GAAa,EAAU,EACjE,IAAqD,CACzD;KACE,mBAAmB;KACnB,qBAAqB,EAAwB;KAC7C,cAAc,EAAY;KAC3B,CACF,EAEG;AACJ,QAAI,EAAO,cAAc;KACvB,IAAM,IAAY,MAAM,EACtB,GACA,EAAO,aACR;AAED,KADA,IAAkB,EAAU,kBAC5B,EAA0B,KAAK;MAC7B,mBAAmB,EAAO;MAC1B,qBAAqB,EAAwB;MAC7C,cAAc,EAAU;MACzB,CAAC;;AAGJ,MACE,GACA,EAAY,kBACZ,EACD;;KAIC,KACJ,GACA,GACA,MACG;AAQH,
|
|
1
|
+
{"version":3,"file":"SynapseVideo.js","names":[],"sources":["../../../src/components/widgets/SynapseVideo.tsx"],"sourcesContent":["import { getEntity, getFiles } from '@/synapse-client/SynapseClient'\nimport { SynapseConstants } from '@/utils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport {\n BatchFileRequest,\n BatchFileResult,\n FileEntity,\n FileHandleAssociateType,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\nimport { useEffect, useState } from 'react'\n\nexport type SynapseVideoProps = {\n params: {\n width?: string\n height?: string\n videoId?: string\n vimeoId?: string\n oggSynapseId?: string\n mp4SynapseId?: string\n webmSynapseId?: string\n vttSynapseId?: string\n }\n}\n\nexport default function SynapseVideo({ params }: SynapseVideoProps) {\n const { accessToken, isAuthenticated } = useSynapseContext()\n const [externalVideoUrl, setExternalVideoUrl] = useState<string>()\n const [synapseVideoPresignedUrl, setSynapseVideoPresignedUrl] =\n useState<string>()\n const [synapseVideoVttPresignedUrl, setSynapseVideoVttPresignedUrl] =\n useState<string>()\n\n const videoWidth = params.width ?? ''\n const videoHeight = params.height ?? ''\n useEffect(() => {\n const getVideo = async () => {\n if (params.videoId)\n setExternalVideoUrl(`https://www.youtube.com/embed/${params.videoId}`)\n else if (params.vimeoId)\n setExternalVideoUrl(`https://player.vimeo.com/video/${params.vimeoId}`)\n else {\n const videoKey =\n params.oggSynapseId || params.mp4SynapseId || params.webmSynapseId\n\n const videoEntity = await getEntity<FileEntity>(accessToken, videoKey!)\n const fileHandleAssociationList: FileHandleAssociation[] = [\n {\n associateObjectId: videoKey!,\n associateObjectType: FileHandleAssociateType.FileEntity,\n fileHandleId: videoEntity.dataFileHandleId,\n },\n ]\n\n let vttFileHandleId: string | undefined\n if (params.vttSynapseId) {\n const vttEntity = await getEntity<FileEntity>(\n accessToken,\n params.vttSynapseId,\n )\n vttFileHandleId = vttEntity.dataFileHandleId\n fileHandleAssociationList.push({\n associateObjectId: params.vttSynapseId,\n associateObjectType: FileHandleAssociateType.FileEntity,\n fileHandleId: vttEntity.dataFileHandleId,\n })\n }\n\n getSynapseFiles(\n fileHandleAssociationList,\n videoEntity.dataFileHandleId,\n vttFileHandleId,\n )\n }\n }\n\n const getSynapseFiles = (\n fileHandleAssociationList: FileHandleAssociation[],\n videoFileHandleId: string,\n vttFileHandleId?: string,\n ) => {\n const request: BatchFileRequest = {\n includeFileHandles: false,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n requestedFiles: fileHandleAssociationList,\n }\n\n getFiles(request, accessToken)\n .then((data: BatchFileResult) => {\n const videoFile = data.requestedFiles.find(\n el => el.fileHandleId === videoFileHandleId,\n )\n if (videoFile?.preSignedURL) {\n setSynapseVideoPresignedUrl(videoFile.preSignedURL)\n }\n\n if (vttFileHandleId) {\n const vttFile = data.requestedFiles.find(\n el => el.fileHandleId === vttFileHandleId,\n )\n if (vttFile?.preSignedURL) {\n setSynapseVideoVttPresignedUrl(vttFile.preSignedURL)\n }\n }\n })\n .catch(err => {\n console.error('Error on getting video ', err)\n })\n }\n getVideo()\n }, [externalVideoUrl, params, accessToken, videoHeight, videoWidth])\n\n if (!isAuthenticated && !externalVideoUrl) {\n // if not logged in, show login button\n return (\n <p>\n You will need to\n <button\n data-testid=\"video-login\"\n className={`${SynapseConstants.SRC_SIGN_IN_CLASS} sign-in-btn default\n `}\n >\n Sign in\n </button>\n in for access to that resource.\n </p>\n )\n }\n if (synapseVideoPresignedUrl) {\n return (\n <video\n controls\n width={videoWidth}\n height={videoHeight}\n data-testid=\"synapse-video-url\"\n crossOrigin=\"anonymous\"\n >\n <source src={synapseVideoPresignedUrl} />\n {synapseVideoVttPresignedUrl && (\n <track src={synapseVideoVttPresignedUrl} kind=\"subtitles\" default />\n )}\n It does not support the HTML5 Video element.\n </video>\n )\n } else if (externalVideoUrl) {\n return (\n <iframe\n title=\"video frame\"\n src={externalVideoUrl}\n width={videoWidth}\n height={videoHeight}\n ></iframe>\n )\n } else {\n return <></>\n }\n}\n"],"mappings":";;;;;;;;AAyBA,SAAwB,EAAa,EAAE,aAA6B;CAClE,IAAM,EAAE,gBAAa,uBAAoB,GAAmB,EACtD,CAAC,GAAkB,KAAuB,GAAkB,EAC5D,CAAC,GAA0B,KAC/B,GAAkB,EACd,CAAC,GAA6B,KAClC,GAAkB,EAEd,IAAa,EAAO,SAAS,IAC7B,IAAc,EAAO,UAAU;AAyHnC,QAxHF,QAAgB;EACd,IAAM,IAAW,YAAY;AAC3B,OAAI,EAAO,QACT,GAAoB,iCAAiC,EAAO,UAAU;YAC/D,EAAO,QACd,GAAoB,kCAAkC,EAAO,UAAU;QACpE;IACH,IAAM,IACJ,EAAO,gBAAgB,EAAO,gBAAgB,EAAO,eAEjD,IAAc,MAAM,EAAsB,GAAa,EAAU,EACjE,IAAqD,CACzD;KACE,mBAAmB;KACnB,qBAAqB,EAAwB;KAC7C,cAAc,EAAY;KAC3B,CACF,EAEG;AACJ,QAAI,EAAO,cAAc;KACvB,IAAM,IAAY,MAAM,EACtB,GACA,EAAO,aACR;AAED,KADA,IAAkB,EAAU,kBAC5B,EAA0B,KAAK;MAC7B,mBAAmB,EAAO;MAC1B,qBAAqB,EAAwB;MAC7C,cAAc,EAAU;MACzB,CAAC;;AAGJ,MACE,GACA,EAAY,kBACZ,EACD;;KAIC,KACJ,GACA,GACA,MACG;AAQH,KAAS;IANP,oBAAoB;IACpB,sBAAsB;IACtB,6BAA6B;IAC7B,gBAAgB;IAGT,EAAS,EAAY,CAC3B,MAAM,MAA0B;IAC/B,IAAM,IAAY,EAAK,eAAe,MACpC,MAAM,EAAG,iBAAiB,EAC3B;AAKD,QAJI,GAAW,gBACb,EAA4B,EAAU,aAAa,EAGjD,GAAiB;KACnB,IAAM,IAAU,EAAK,eAAe,MAClC,MAAM,EAAG,iBAAiB,EAC3B;AACD,KAAI,GAAS,gBACX,EAA+B,EAAQ,aAAa;;KAGxD,CACD,OAAM,MAAO;AACZ,YAAQ,MAAM,2BAA2B,EAAI;KAC7C;;AAEN,KAAU;IACT;EAAC;EAAkB;EAAQ;EAAa;EAAa;EAAW,CAAC,EAEhE,CAAC,KAAmB,CAAC,IAGrB,kBAAC,KAAD,EAAA,UAAA;EAAG;EAED,kBAAC,UAAD;GACE,eAAY;GACZ,WAAW,GAAG,EAAmC;;aAElD;GAEQ,CAAA;;EAEP,EAAA,CAAA,GAGJ,IAEA,kBAAC,SAAD;EACE,UAAA;EACA,OAAO;EACP,QAAQ;EACR,eAAY;EACZ,aAAY;YALd;GAOE,kBAAC,UAAD,EAAQ,KAAK,GAA4B,CAAA;GACxC,KACC,kBAAC,SAAD;IAAO,KAAK;IAA6B,MAAK;IAAY,SAAA;IAAU,CAAA;GACpE;GAEI;MAED,IAEP,kBAAC,UAAD;EACE,OAAM;EACN,KAAK;EACL,OAAO;EACP,QAAQ;EACA,CAAA,GAGL,kBAAA,GAAA,EAAK,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FacetNavPanel.js","names":[],"sources":["../../../../src/components/widgets/facet-nav/FacetNavPanel.tsx"],"sourcesContent":["import StyledFormControl from '@/components/styled/StyledFormControl'\nimport SynapseClient from '@/synapse-client'\nimport { SynapseConstants } from '@/utils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { getCorrespondingColumnForFacet } from '@/utils/functions/queryUtils'\nimport { InfoOutlined } from '@mui/icons-material'\nimport {\n Box,\n InputLabel,\n MenuItem,\n Select,\n Stack,\n Tooltip,\n} from '@mui/material'\nimport {\n ColumnTypeEnum,\n FacetColumnRequest,\n FacetColumnResultValueCount,\n FacetColumnResultValues,\n} from '@sage-bionetworks/synapse-types'\nimport { useQuery } from '@tanstack/react-query'\nimport type Plotly from 'plotly.js-basic-dist'\nimport { useMemo, useState } from 'react'\nimport { getContrastColorPalette } from '../../ColorGradient/ColorGradient'\nimport { ConfirmationDialog } from '../../ConfirmationDialog/ConfirmationDialog'\nimport loadingScreen from '../../LoadingScreen/LoadingScreen'\nimport Plot from '../../Plot/Plot'\nimport PlotPanelHeader from '../../Plot/PlotPanelHeader'\nimport { useQueryVisualizationContext } from '../../QueryVisualizationWrapper'\nimport { useGetQueryMetadata } from '../../QueryWrapper/useGetQueryMetadata'\nimport { EnumFacetFilter } from '../query-filter/EnumFacetFilter/EnumFacetFilter'\nimport { FacetPlotLegendList } from './FacetPlotLegendList'\nimport { FacetWithLabel, truncate } from './FacetPlotLegendUtils'\nimport { useMeasure } from '@react-hookz/web'\n\nexport type FacetNavPanelProps = {\n applyChangesToGraphSlice: (\n facet: FacetColumnResultValues,\n value: FacetColumnResultValueCount | undefined,\n isSelected: boolean,\n ) => void\n applyChangesToFacetFilter: (facets: FacetColumnRequest[]) => void\n index: number\n facetToPlot: FacetColumnResultValues\n plotType: PlotType\n onSetPlotType: (plotType: PlotType) => void\n onHide: () => void\n isModalView: boolean\n onCloseModal?: () => void\n}\n\nconst maxLabelLength: number = 19\n\n// STACKED_HORIZONTAL_BAR corresponds to a bar chart where we just want to show the proportion (like a pie chart)\nexport type PlotType = 'PIE' | 'BAR' | 'STACKED_HORIZONTAL_BAR'\n\nconst layout: Partial<Plotly.Layout> = {\n showlegend: false,\n annotations: [],\n margin: { l: 0, r: 0, b: 0, t: 0, pad: 0 },\n yaxis: {\n visible: false,\n showgrid: false,\n },\n xaxis: {\n visible: false,\n showgrid: false,\n },\n}\n\nexport type GraphData = {\n data: Plotly.Data[]\n labels: FacetWithLabel[]\n colors: string[]\n}\n\nexport async function extractPlotDataArray(\n facetToPlot: FacetColumnResultValues,\n columnType: ColumnTypeEnum | undefined,\n index: number,\n plotType: PlotType,\n accessToken?: string,\n) {\n const colorPalette = getContrastColorPalette(\n // Use only the odd palette, using the same offset for all plots until palettes are improved.\n // See PORTALS-2916\n 'odd', // index % 2 === 0 ? 'even' : 'odd',\n 0, // Math.floor(index / 2),\n facetToPlot.facetValues.length,\n )\n\n const getLabels = async (\n facetValues: FacetColumnResultValueCount[],\n columnType?: ColumnTypeEnum,\n accessToken?: string,\n ) => {\n const map = new Map<string, string>()\n map.set(\n SynapseConstants.VALUE_NOT_SET,\n SynapseConstants.FRIENDLY_VALUE_NOT_SET,\n )\n // Filter out empties\n const filteredValues = facetValues\n .map(value => value.value)\n .filter(val => val !== SynapseConstants.VALUE_NOT_SET)\n if (\n columnType === ColumnTypeEnum.ENTITYID ||\n columnType === ColumnTypeEnum.ENTITYID_LIST\n ) {\n // TODO: Pagination\n const response = await SynapseClient.getEntityHeadersByIds(\n filteredValues,\n accessToken,\n )\n for (const header of response.results) {\n map.set(header.id, header.name)\n }\n } else if (\n columnType === ColumnTypeEnum.USERID ||\n columnType === ColumnTypeEnum.USERID_LIST\n ) {\n const response = await SynapseClient.getGroupHeadersBatch(\n filteredValues,\n accessToken,\n )\n for (const header of response.children) {\n map.set(header.ownerId, header.userName)\n }\n }\n\n return facetValues.map(facetValue => ({\n facet: facetValue,\n label: getLabel(facetValue, false, map),\n truncatedLabel: getLabel(facetValue, true, map),\n count: facetValue.count,\n }))\n }\n\n const getLabel = (\n facetValue: FacetColumnResultValueCount,\n truncateFlag: boolean,\n labelMap: Map<string, string>,\n ): string => {\n let label = labelMap.get(facetValue.value) ?? facetValue.value\n if (truncateFlag) {\n label = truncate(label, maxLabelLength)!\n }\n return label\n }\n\n const labels = await getLabels(\n facetToPlot.facetValues,\n columnType,\n accessToken,\n )\n const text = labels.map(el => el.truncatedLabel)\n\n const anyFacetsSelected = facetToPlot.facetValues.some(\n value => value.isSelected,\n )\n const selectionAwareColorPalette = anyFacetsSelected\n ? facetToPlot.facetValues.map((facetValue, index) =>\n facetValue.isSelected\n ? colorPalette[index]\n : colorPalette[index]\n .replace('rgb(', 'rgba(')\n .replace(')', ', 0.25)'),\n )\n : colorPalette\n const counts: Plotly.Datum[] = facetToPlot.facetValues.map(\n facet => facet.count,\n )\n let x: Plotly.Datum[] | Plotly.Datum[][] | Plotly.TypedArray | undefined\n\n if (plotType === 'BAR') {\n x = facetToPlot.facetValues.map(\n facet =>\n labels.find(label => label.facet === facet)?.label ?? facet.value,\n )\n } else if (plotType === 'STACKED_HORIZONTAL_BAR') {\n x = counts\n }\n\n let y: Plotly.Datum[] | Plotly.Datum[][] | Plotly.TypedArray | undefined\n if (plotType === 'BAR') {\n y = facetToPlot.facetValues.map(facet => facet.count)\n } else if (plotType === 'STACKED_HORIZONTAL_BAR') {\n y = Array(x?.length).fill('Proportional') // single value for every x value\n }\n\n const singleChartData: Plotly.Data = {\n values: plotType === 'PIE' ? counts : undefined,\n labels: labels.map(el => el.label),\n text,\n x,\n y,\n orientation: plotType === 'STACKED_HORIZONTAL_BAR' ? 'h' : 'v',\n // @ts-expect-error\n facetEnumerationValues: facetToPlot.facetValues.map(\n facetValue => facetValue.value,\n ),\n name: facetToPlot.columnName,\n textposition:\n plotType === 'STACKED_HORIZONTAL_BAR' || plotType === 'BAR'\n ? 'none'\n : 'inside',\n hovertemplate:\n plotType === 'PIE'\n ? '<b>%{text}</b><br>%{value} (%{percent})<br><extra></extra>'\n : '<b>%{text}: </b><br>%{value} <br><extra></extra>',\n textinfo: 'none',\n type: plotType === 'PIE' ? 'pie' : 'bar',\n pull:\n plotType === 'PIE'\n ? facetToPlot.facetValues.map(facetValue =>\n facetValue.isSelected ? 0.1 : 0,\n )\n : undefined,\n selectedpoints: anyFacetsSelected\n ? facetToPlot.facetValues\n .map((facetValue, index) => (facetValue.isSelected ? index : -1))\n .filter(index => index !== -1)\n : undefined,\n selected: { marker: { opacity: 1 } },\n unselected: { marker: { opacity: 0.25 } },\n\n marker: {\n colors: plotType === 'PIE' ? selectionAwareColorPalette : undefined,\n color: plotType === 'PIE' ? undefined : selectionAwareColorPalette,\n },\n }\n const result = {\n data: [singleChartData],\n labels,\n colors:\n plotType === 'PIE'\n ? ((singleChartData as any).marker?.colors as string[])\n : ((singleChartData as any).marker?.color as string[]),\n }\n return result\n}\n\nconst applyFacetFilter = (\n event: Plotly.PlotMouseEvent,\n allFacetValues: FacetColumnResultValues,\n callbackApplyFn: FacetNavPanelProps['applyChangesToGraphSlice'],\n) => {\n if (event.points && event.points[0]) {\n const plotPointData: any = event.points[0]\n const facetValueClickedValue =\n plotPointData.data.facetEnumerationValues[plotPointData.pointNumber]\n const facetValueClicked = allFacetValues.facetValues.find(\n facet => facet.value === facetValueClickedValue,\n )\n callbackApplyFn(\n allFacetValues,\n facetValueClicked,\n !facetValueClicked!.isSelected,\n )\n }\n}\n\nexport function getPlotStyle(\n parentWidth: number | null | undefined,\n plotType: PlotType,\n maxHeight: number,\n): { width: string; height: string } {\n if (parentWidth != undefined) {\n let quotient = 1\n switch (plotType) {\n case 'BAR':\n quotient = 0.8\n break\n case 'PIE':\n quotient = 0.6\n break\n case 'STACKED_HORIZONTAL_BAR':\n quotient = 1\n break\n }\n const width = parentWidth ? parentWidth * quotient : 200\n let height = plotType === 'PIE' ? width : width / 3\n // max height of .PlotsContainer row col* is 200px, so the effective plot height max is around 150 unless it's expanded\n if (height > maxHeight) {\n height = maxHeight\n }\n return {\n width: `${width}px`,\n height: `${height}px`,\n }\n }\n //else parent width is undefined\n return {\n width: '100%',\n height: `${maxHeight}px`,\n }\n}\n\nfunction FacetNavPanel(props: FacetNavPanelProps) {\n const {\n onHide,\n isModalView,\n applyChangesToGraphSlice,\n index,\n facetToPlot,\n plotType,\n onSetPlotType,\n } = props\n const { accessToken } = useSynapseContext()\n const { data: queryMetadata, isLoading: isLoadingQueryMetadata } =\n useGetQueryMetadata()\n\n const [plotContainerMeasurements, plotContainerRef] =\n useMeasure<HTMLDivElement>()\n const { getColumnDisplayName } = useQueryVisualizationContext()\n\n const [showModal, setShowModal] = useState(false)\n\n const plotTitle = getColumnDisplayName(\n facetToPlot.columnName,\n facetToPlot.jsonPath,\n )\n\n const columnModel = useMemo(\n () =>\n getCorrespondingColumnForFacet(\n facetToPlot,\n queryMetadata?.columnModels ?? [],\n ),\n [queryMetadata?.columnModels, facetToPlot],\n )\n const columnType = columnModel?.columnType as ColumnTypeEnum\n\n const { data: plotData } = useQuery({\n queryKey: [\n 'extractPlotDataArray',\n facetToPlot,\n columnType,\n index,\n plotType,\n accessToken,\n ],\n\n queryFn: () =>\n extractPlotDataArray(\n facetToPlot,\n columnType,\n index,\n plotType,\n accessToken,\n ),\n\n enabled: !!facetToPlot,\n })\n\n /* rendering functions */\n const chartSelectionToggle = (\n <StyledFormControl fullWidth>\n <InputLabel>Chart Type</InputLabel>\n <Select\n value={plotType}\n onChange={e => {\n onSetPlotType(e.target.value as PlotType)\n }}\n >\n <MenuItem value={'BAR'}>Bar Chart</MenuItem>\n <MenuItem value={'PIE'}>Pie Chart</MenuItem>\n </Select>\n </StyledFormControl>\n )\n\n if (\n (!queryMetadata && isLoadingQueryMetadata) ||\n !facetToPlot ||\n !columnModel\n ) {\n return (\n <div className=\"SRC-loadingContainer SRC-centerContentColumn\">\n {loadingScreen}\n </div>\n )\n } else {\n return (\n <>\n <ConfirmationDialog\n open={showModal}\n onCancel={() => setShowModal(false)}\n title={plotTitle ?? ''}\n content={<FacetNavPanel {...props} isModalView={true} />}\n hasCancelButton={false}\n confirmButtonProps={{ children: 'Apply Filters' }}\n onConfirm={() => setShowModal(false)}\n maxWidth={'md'}\n />\n <div\n role=\"figure\"\n className={`FacetNavPanel${isModalView ? '--expanded' : ''}`}\n >\n {!isModalView && (\n <PlotPanelHeader\n data={queryMetadata}\n isLoading={isLoadingQueryMetadata}\n title={plotTitle}\n facetToPlot={facetToPlot}\n onHide={onHide}\n setShowModal={setShowModal}\n />\n )}\n {isModalView && (\n <Stack\n sx={{\n gap: 2,\n }}\n >\n <StyledFormControl>\n <InputLabel\n sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}\n >\n <span>Filter All Data By</span>\n <Tooltip title=\"Selecting items in this dropdown will affect all facets on the Explore page.\">\n <InfoOutlined className=\"SRC-hand-cursor SRC-secondary-text-color\" />\n </Tooltip>\n </InputLabel>\n <EnumFacetFilter\n facet={facetToPlot}\n containerAs=\"Dropdown\"\n dropdownType=\"SelectBox\"\n />\n </StyledFormControl>\n {chartSelectionToggle}\n </Stack>\n )}\n <Box\n sx={{\n display: 'grid',\n gridTemplateColumns: '50% 50%',\n alignItems: 'center',\n }}\n role=\"graphics-object\"\n className=\"FacetNavPanel__body\"\n >\n <div ref={plotContainerRef}>\n <Plot\n key={`${facetToPlot.columnName}-${facetToPlot.jsonPath}-${plotType}-${plotContainerMeasurements?.width}`}\n layout={layout}\n data={plotData?.data ?? []}\n style={getPlotStyle(\n plotContainerMeasurements?.width,\n plotType,\n isModalView ? 300 : 150,\n )}\n config={{ displayModeBar: false }}\n onClick={evt =>\n applyFacetFilter(evt, facetToPlot, applyChangesToGraphSlice)\n }\n />\n </div>\n <FacetPlotLegendList\n labels={plotData?.labels}\n colors={plotData?.colors}\n isExpanded={isModalView}\n />\n </Box>\n </div>\n </>\n )\n }\n}\n\nexport default FacetNavPanel\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,IAAM,IAAyB,IAKzB,IAAiC;CACrC,YAAY;CACZ,aAAa,EAAE;CACf,QAAQ;EAAE,GAAG;EAAG,GAAG;EAAG,GAAG;EAAG,GAAG;EAAG,KAAK;EAAG;CAC1C,OAAO;EACL,SAAS;EACT,UAAU;EACX;CACD,OAAO;EACL,SAAS;EACT,UAAU;EACX;CACF;AAQD,eAAsB,EACpB,GACA,GACA,GACA,GACA,GACA;CACA,IAAM,IAAe,EAGnB,OACA,GACA,EAAY,YAAY,OACzB,EAEK,IAAY,OAChB,GACA,GACA,MACG;EACH,IAAM,oBAAM,IAAI,KAAqB;AACrC,IAAI,IACF,GACA,EACD;EAED,IAAM,IAAiB,EACpB,KAAI,MAAS,EAAM,MAAM,CACzB,QAAO,MAAO,MAAQ,EAA+B;AACxD,MACE,MAAe,EAAe,YAC9B,MAAe,EAAe,eAC9B;GAEA,IAAM,IAAW,MAAM,EAAc,sBACnC,GACA,EACD;AACD,QAAK,IAAM,KAAU,EAAS,QAC5B,GAAI,IAAI,EAAO,IAAI,EAAO,KAAK;aAGjC,MAAe,EAAe,UAC9B,MAAe,EAAe,aAC9B;GACA,IAAM,IAAW,MAAM,EAAc,qBACnC,GACA,EACD;AACD,QAAK,IAAM,KAAU,EAAS,SAC5B,GAAI,IAAI,EAAO,SAAS,EAAO,SAAS;;AAI5C,SAAO,EAAY,KAAI,OAAe;GACpC,OAAO;GACP,OAAO,EAAS,GAAY,IAAO,EAAI;GACvC,gBAAgB,EAAS,GAAY,IAAM,EAAI;GAC/C,OAAO,EAAW;GACnB,EAAE;IAGC,KACJ,GACA,GACA,MACW;EACX,IAAI,IAAQ,EAAS,IAAI,EAAW,MAAM,IAAI,EAAW;AAIzD,SAHI,MACF,IAAQ,EAAS,GAAO,EAAe,GAElC;IAGH,IAAS,MAAM,EACnB,EAAY,aACZ,GACA,EACD,EACK,IAAO,EAAO,KAAI,MAAM,EAAG,eAAe,EAE1C,IAAoB,EAAY,YAAY,MAChD,MAAS,EAAM,WAChB,EACK,IAA6B,IAC/B,EAAY,YAAY,KAAK,GAAY,MACvC,EAAW,aACP,EAAa,KACb,EAAa,GACV,QAAQ,QAAQ,QAAQ,CACxB,QAAQ,KAAK,UAAU,CAC/B,GACD,GACE,IAAyB,EAAY,YAAY,KACrD,MAAS,EAAM,MAChB,EACG;AAEJ,CAAI,MAAa,QACf,IAAI,EAAY,YAAY,KAC1B,MACE,EAAO,MAAK,MAAS,EAAM,UAAU,EAAM,EAAE,SAAS,EAAM,MAC/D,GACQ,MAAa,6BACtB,IAAI;CAGN,IAAI;AACJ,CAAI,MAAa,QACf,IAAI,EAAY,YAAY,KAAI,MAAS,EAAM,MAAM,GAC5C,MAAa,6BACtB,IAAI,MAAM,GAAG,OAAO,CAAC,KAAK,eAAe;CAG3C,IAAM,IAA+B;EACnC,QAAQ,MAAa,QAAQ,IAAS,KAAA;EACtC,QAAQ,EAAO,KAAI,MAAM,EAAG,MAAM;EAClC;EACA;EACA;EACA,aAAa,MAAa,2BAA2B,MAAM;EAE3D,wBAAwB,EAAY,YAAY,KAC9C,MAAc,EAAW,MAC1B;EACD,MAAM,EAAY;EAClB,cACE,MAAa,4BAA4B,MAAa,QAClD,SACA;EACN,eACE,MAAa,QACT,+DACA;EACN,UAAU;EACV,MAAM,MAAa,QAAQ,QAAQ;EACnC,MACE,MAAa,QACT,EAAY,YAAY,KAAI,MAC1B,EAAW,aAAa,KAAM,EAC/B,GACD,KAAA;EACN,gBAAgB,IACZ,EAAY,YACT,KAAK,GAAY,MAAW,EAAW,aAAa,IAAQ,GAAI,CAChE,QAAO,MAAS,MAAU,GAAG,GAChC,KAAA;EACJ,UAAU,EAAE,QAAQ,EAAE,SAAS,GAAG,EAAE;EACpC,YAAY,EAAE,QAAQ,EAAE,SAAS,KAAM,EAAE;EAEzC,QAAQ;GACN,QAAQ,MAAa,QAAQ,IAA6B,KAAA;GAC1D,OAAO,MAAa,QAAQ,KAAA,IAAY;GACzC;EACF;AASD,QARe;EACb,MAAM,CAAC,EAAgB;EACvB;EACA,QACE,MAAa,QACP,EAAwB,QAAQ,SAChC,EAAwB,QAAQ;EACzC;;AAIH,IAAM,KACJ,GACA,GACA,MACG;AACH,KAAI,EAAM,UAAU,EAAM,OAAO,IAAI;EACnC,IAAM,IAAqB,EAAM,OAAO,IAClC,IACJ,EAAc,KAAK,uBAAuB,EAAc,cACpD,IAAoB,EAAe,YAAY,MACnD,MAAS,EAAM,UAAU,EAC1B;AACD,IACE,GACA,GACA,CAAC,EAAmB,WACrB;;;AAIL,SAAgB,EACd,GACA,GACA,GACmC;AACnC,KAAI,KAAe,MAAW;EAC5B,IAAI,IAAW;AACf,UAAQ,GAAR;GACE,KAAK;AACH,QAAW;AACX;GACF,KAAK;AACH,QAAW;AACX;GACF,KAAK;AACH,QAAW;AACX;;EAEJ,IAAM,IAAQ,IAAc,IAAc,IAAW,KACjD,IAAS,MAAa,QAAQ,IAAQ,IAAQ;AAKlD,SAHI,IAAS,MACX,IAAS,IAEJ;GACL,OAAO,GAAG,EAAM;GAChB,QAAQ,GAAG,EAAO;GACnB;;AAGH,QAAO;EACL,OAAO;EACP,QAAQ,GAAG,EAAU;EACtB;;AAGH,SAAS,EAAc,GAA2B;CAChD,IAAM,EACJ,WACA,gBACA,6BACA,UACA,gBACA,aACA,qBACE,GACE,EAAE,mBAAgB,GAAmB,EACrC,EAAE,MAAM,GAAe,WAAW,MACtC,GAAqB,EAEjB,CAAC,GAA2B,KAChC,GAA4B,EACxB,EAAE,4BAAyB,GAA8B,EAEzD,CAAC,GAAW,KAAgB,EAAS,GAAM,EAE3C,IAAY,EAChB,EAAY,YACZ,EAAY,SACb,EAEK,IAAc,QAEhB,EACE,GACA,GAAe,gBAAgB,EAAE,CAClC,EACH,CAAC,GAAe,cAAc,EAAY,CAC3C,EACK,IAAa,GAAa,YAE1B,EAAE,MAAM,MAAa,EAAS;EAClC,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACD;EAED,eACE,EACE,GACA,GACA,GACA,GACA,EACD;EAEH,SAAS,CAAC,CAAC;EACZ,CAAC,EAGI,IACJ,kBAAC,GAAD;EAAmB,WAAA;YAAnB,CACE,kBAAC,GAAD,EAAA,UAAY,cAAuB,CAAA,EACnC,kBAAC,GAAD;GACE,OAAO;GACP,WAAU,MAAK;AACb,MAAc,EAAE,OAAO,MAAkB;;aAH7C,CAME,kBAAC,GAAD;IAAU,OAAO;cAAO;IAAoB,CAAA,EAC5C,kBAAC,GAAD;IAAU,OAAO;cAAO;IAAoB,CAAA,CACrC;KACS;;AAcpB,QAVC,CAAC,KAAiB,KACnB,CAAC,KACD,CAAC,IAGC,kBAAC,OAAD;EAAK,WAAU;YACZ;EACG,CAAA,GAIN,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;EACE,MAAM;EACN,gBAAgB,EAAa,GAAM;EACnC,OAAO,KAAa;EACpB,SAAS,kBAAC,GAAD;GAAe,GAAI;GAAO,aAAa;GAAQ,CAAA;EACxD,iBAAiB;EACjB,oBAAoB,EAAE,UAAU,iBAAiB;EACjD,iBAAiB,EAAa,GAAM;EACpC,UAAU;EACV,CAAA,EACF,kBAAC,OAAD;EACE,MAAK;EACL,WAAW,gBAAgB,IAAc,eAAe;YAF1D;GAIG,CAAC,KACA,kBAAC,GAAD;IACE,MAAM;IACN,WAAW;IACX,OAAO;IACM;IACL;IACM;IACd,CAAA;GAEH,KACC,kBAAC,GAAD;IACE,IAAI,EACF,KAAK,GACN;cAHH,CAKE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KACE,IAAI;MAAE,SAAS;MAAQ,YAAY;MAAU,KAAK;MAAK;eADzD,CAGE,kBAAC,QAAD,EAAA,UAAM,sBAAyB,CAAA,EAC/B,kBAAC,GAAD;MAAS,OAAM;gBACb,kBAAC,GAAD,EAAc,WAAU,4CAA6C,CAAA;MAC7D,CAAA,CACC;QACb,kBAAC,GAAD;KACE,OAAO;KACP,aAAY;KACZ,cAAa;KACb,CAAA,CACgB,EAAA,CAAA,EACnB,EACK;;GAEV,kBAAC,GAAD;IACE,IAAI;KACF,SAAS;KACT,qBAAqB;KACrB,YAAY;KACb;IACD,MAAK;IACL,WAAU;cAPZ,CASE,kBAAC,OAAD;KAAK,KAAK;eACR,kBAAC,GAAD;MAEU;MACR,MAAM,GAAU,QAAQ,EAAE;MAC1B,OAAO,EACL,GAA2B,OAC3B,GACA,IAAc,MAAM,IACrB;MACD,QAAQ,EAAE,gBAAgB,IAAO;MACjC,UAAS,MACP,EAAiB,GAAK,GAAa,EAAyB;MAE9D,EAZK,GAAG,EAAY,WAAW,GAAG,EAAY,SAAS,GAAG,EAAS,GAAG,GAA2B,QAYjG;KACE,CAAA,EACN,kBAAC,GAAD;KACE,QAAQ,GAAU;KAClB,QAAQ,GAAU;KAClB,YAAY;KACZ,CAAA,CACE;;GACF;IACL,EAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"FacetNavPanel.js","names":[],"sources":["../../../../src/components/widgets/facet-nav/FacetNavPanel.tsx"],"sourcesContent":["import StyledFormControl from '@/components/styled/StyledFormControl'\nimport SynapseClient from '@/synapse-client'\nimport { SynapseConstants } from '@/utils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { getCorrespondingColumnForFacet } from '@/utils/functions/queryUtils'\nimport { InfoOutlined } from '@mui/icons-material'\nimport {\n Box,\n InputLabel,\n MenuItem,\n Select,\n Stack,\n Tooltip,\n} from '@mui/material'\nimport {\n ColumnTypeEnum,\n FacetColumnRequest,\n FacetColumnResultValueCount,\n FacetColumnResultValues,\n} from '@sage-bionetworks/synapse-types'\nimport { useQuery } from '@tanstack/react-query'\nimport type Plotly from 'plotly.js-basic-dist'\nimport { useMemo, useState } from 'react'\nimport { getContrastColorPalette } from '../../ColorGradient/ColorGradient'\nimport { ConfirmationDialog } from '../../ConfirmationDialog/ConfirmationDialog'\nimport loadingScreen from '../../LoadingScreen/LoadingScreen'\nimport Plot from '../../Plot/Plot'\nimport PlotPanelHeader from '../../Plot/PlotPanelHeader'\nimport { useQueryVisualizationContext } from '../../QueryVisualizationWrapper'\nimport { useGetQueryMetadata } from '../../QueryWrapper/useGetQueryMetadata'\nimport { EnumFacetFilter } from '../query-filter/EnumFacetFilter/EnumFacetFilter'\nimport { FacetPlotLegendList } from './FacetPlotLegendList'\nimport { FacetWithLabel, truncate } from './FacetPlotLegendUtils'\nimport { useMeasure } from '@react-hookz/web'\n\nexport type FacetNavPanelProps = {\n applyChangesToGraphSlice: (\n facet: FacetColumnResultValues,\n value: FacetColumnResultValueCount | undefined,\n isSelected: boolean,\n ) => void\n applyChangesToFacetFilter: (facets: FacetColumnRequest[]) => void\n index: number\n facetToPlot: FacetColumnResultValues\n plotType: PlotType\n onSetPlotType: (plotType: PlotType) => void\n onHide: () => void\n isModalView: boolean\n onCloseModal?: () => void\n}\n\nconst maxLabelLength: number = 19\n\n// STACKED_HORIZONTAL_BAR corresponds to a bar chart where we just want to show the proportion (like a pie chart)\nexport type PlotType = 'PIE' | 'BAR' | 'STACKED_HORIZONTAL_BAR'\n\nconst layout: Partial<Plotly.Layout> = {\n showlegend: false,\n annotations: [],\n margin: { l: 0, r: 0, b: 0, t: 0, pad: 0 },\n yaxis: {\n visible: false,\n showgrid: false,\n },\n xaxis: {\n visible: false,\n showgrid: false,\n },\n}\n\nexport type GraphData = {\n data: Plotly.Data[]\n labels: FacetWithLabel[]\n colors: string[]\n}\n\nexport async function extractPlotDataArray(\n facetToPlot: FacetColumnResultValues,\n columnType: ColumnTypeEnum | undefined,\n index: number,\n plotType: PlotType,\n accessToken?: string,\n) {\n const colorPalette = getContrastColorPalette(\n // Use only the odd palette, using the same offset for all plots until palettes are improved.\n // See PORTALS-2916\n 'odd', // index % 2 === 0 ? 'even' : 'odd',\n 0, // Math.floor(index / 2),\n facetToPlot.facetValues.length,\n )\n\n const getLabels = async (\n facetValues: FacetColumnResultValueCount[],\n columnType?: ColumnTypeEnum,\n accessToken?: string,\n ) => {\n const map = new Map<string, string>()\n map.set(\n SynapseConstants.VALUE_NOT_SET,\n SynapseConstants.FRIENDLY_VALUE_NOT_SET,\n )\n // Filter out empties\n const filteredValues = facetValues\n .map(value => value.value)\n .filter(val => val !== SynapseConstants.VALUE_NOT_SET)\n if (\n columnType === ColumnTypeEnum.ENTITYID ||\n columnType === ColumnTypeEnum.ENTITYID_LIST\n ) {\n // TODO: Pagination\n const response = await SynapseClient.getEntityHeadersByIds(\n filteredValues,\n accessToken,\n )\n for (const header of response.results) {\n map.set(header.id, header.name)\n }\n } else if (\n columnType === ColumnTypeEnum.USERID ||\n columnType === ColumnTypeEnum.USERID_LIST\n ) {\n const response = await SynapseClient.getGroupHeadersBatch(\n filteredValues,\n accessToken,\n )\n for (const header of response.children) {\n map.set(header.ownerId, header.userName)\n }\n }\n\n return facetValues.map(facetValue => ({\n facet: facetValue,\n label: getLabel(facetValue, false, map),\n truncatedLabel: getLabel(facetValue, true, map),\n count: facetValue.count,\n }))\n }\n\n const getLabel = (\n facetValue: FacetColumnResultValueCount,\n truncateFlag: boolean,\n labelMap: Map<string, string>,\n ): string => {\n let label = labelMap.get(facetValue.value) ?? facetValue.value\n if (truncateFlag) {\n label = truncate(label, maxLabelLength)!\n }\n return label\n }\n\n const labels = await getLabels(\n facetToPlot.facetValues,\n columnType,\n accessToken,\n )\n const text = labels.map(el => el.truncatedLabel)\n\n const anyFacetsSelected = facetToPlot.facetValues.some(\n value => value.isSelected,\n )\n const selectionAwareColorPalette = anyFacetsSelected\n ? facetToPlot.facetValues.map((facetValue, index) =>\n facetValue.isSelected\n ? colorPalette[index]\n : colorPalette[index]\n .replace('rgb(', 'rgba(')\n .replace(')', ', 0.25)'),\n )\n : colorPalette\n const counts: Plotly.Datum[] = facetToPlot.facetValues.map(\n facet => facet.count,\n )\n let x: Plotly.Datum[] | Plotly.Datum[][] | Plotly.TypedArray | undefined\n\n if (plotType === 'BAR') {\n x = facetToPlot.facetValues.map(\n facet =>\n labels.find(label => label.facet === facet)?.label ?? facet.value,\n )\n } else if (plotType === 'STACKED_HORIZONTAL_BAR') {\n x = counts\n }\n\n let y: Plotly.Datum[] | Plotly.Datum[][] | Plotly.TypedArray | undefined\n if (plotType === 'BAR') {\n y = facetToPlot.facetValues.map(facet => facet.count)\n } else if (plotType === 'STACKED_HORIZONTAL_BAR') {\n y = Array(x?.length).fill('Proportional') // single value for every x value\n }\n\n const singleChartData: Plotly.Data = {\n values: plotType === 'PIE' ? counts : undefined,\n labels: labels.map(el => el.label),\n text,\n x,\n y,\n orientation: plotType === 'STACKED_HORIZONTAL_BAR' ? 'h' : 'v',\n // @ts-expect-error\n facetEnumerationValues: facetToPlot.facetValues.map(\n facetValue => facetValue.value,\n ),\n name: facetToPlot.columnName,\n textposition:\n plotType === 'STACKED_HORIZONTAL_BAR' || plotType === 'BAR'\n ? 'none'\n : 'inside',\n hovertemplate:\n plotType === 'PIE'\n ? '<b>%{text}</b><br>%{value} (%{percent})<br><extra></extra>'\n : '<b>%{text}: </b><br>%{value} <br><extra></extra>',\n textinfo: 'none',\n type: plotType === 'PIE' ? 'pie' : 'bar',\n pull:\n plotType === 'PIE'\n ? facetToPlot.facetValues.map(facetValue =>\n facetValue.isSelected ? 0.1 : 0,\n )\n : undefined,\n selectedpoints: anyFacetsSelected\n ? facetToPlot.facetValues\n .map((facetValue, index) => (facetValue.isSelected ? index : -1))\n .filter(index => index !== -1)\n : undefined,\n selected: { marker: { opacity: 1 } },\n unselected: { marker: { opacity: 0.25 } },\n\n marker: {\n colors: plotType === 'PIE' ? selectionAwareColorPalette : undefined,\n color: plotType === 'PIE' ? undefined : selectionAwareColorPalette,\n },\n }\n const result = {\n data: [singleChartData],\n labels,\n colors:\n plotType === 'PIE'\n ? ((singleChartData as any).marker?.colors as string[])\n : ((singleChartData as any).marker?.color as string[]),\n }\n return result\n}\n\nconst applyFacetFilter = (\n event: Plotly.PlotMouseEvent,\n allFacetValues: FacetColumnResultValues,\n callbackApplyFn: FacetNavPanelProps['applyChangesToGraphSlice'],\n) => {\n if (event.points && event.points[0]) {\n const plotPointData: any = event.points[0]\n const facetValueClickedValue =\n plotPointData.data.facetEnumerationValues[plotPointData.pointNumber]\n const facetValueClicked = allFacetValues.facetValues.find(\n facet => facet.value === facetValueClickedValue,\n )\n callbackApplyFn(\n allFacetValues,\n facetValueClicked,\n !facetValueClicked!.isSelected,\n )\n }\n}\n\nexport function getPlotStyle(\n parentWidth: number | null | undefined,\n plotType: PlotType,\n maxHeight: number,\n): { width: string; height: string } {\n if (parentWidth != undefined) {\n let quotient = 1\n switch (plotType) {\n case 'BAR':\n quotient = 0.8\n break\n case 'PIE':\n quotient = 0.6\n break\n case 'STACKED_HORIZONTAL_BAR':\n quotient = 1\n break\n }\n const width = parentWidth ? parentWidth * quotient : 200\n let height = plotType === 'PIE' ? width : width / 3\n // max height of .PlotsContainer row col* is 200px, so the effective plot height max is around 150 unless it's expanded\n if (height > maxHeight) {\n height = maxHeight\n }\n return {\n width: `${width}px`,\n height: `${height}px`,\n }\n }\n //else parent width is undefined\n return {\n width: '100%',\n height: `${maxHeight}px`,\n }\n}\n\nfunction FacetNavPanel(props: FacetNavPanelProps) {\n const {\n onHide,\n isModalView,\n applyChangesToGraphSlice,\n index,\n facetToPlot,\n plotType,\n onSetPlotType,\n } = props\n const { accessToken } = useSynapseContext()\n const { data: queryMetadata, isLoading: isLoadingQueryMetadata } =\n useGetQueryMetadata()\n\n const [plotContainerMeasurements, plotContainerRef] =\n useMeasure<HTMLDivElement>()\n const { getColumnDisplayName } = useQueryVisualizationContext()\n\n const [showModal, setShowModal] = useState(false)\n\n const plotTitle = getColumnDisplayName(\n facetToPlot.columnName,\n facetToPlot.jsonPath,\n )\n\n const columnModel = useMemo(\n () =>\n getCorrespondingColumnForFacet(\n facetToPlot,\n queryMetadata?.columnModels ?? [],\n ),\n [queryMetadata?.columnModels, facetToPlot],\n )\n const columnType = columnModel?.columnType as ColumnTypeEnum\n\n const { data: plotData } = useQuery({\n queryKey: [\n 'extractPlotDataArray',\n facetToPlot,\n columnType,\n index,\n plotType,\n accessToken,\n ],\n\n queryFn: () =>\n extractPlotDataArray(\n facetToPlot,\n columnType,\n index,\n plotType,\n accessToken,\n ),\n\n enabled: !!facetToPlot,\n })\n\n /* rendering functions */\n const chartSelectionToggle = (\n <StyledFormControl fullWidth>\n <InputLabel>Chart Type</InputLabel>\n <Select\n value={plotType}\n onChange={e => {\n onSetPlotType(e.target.value as PlotType)\n }}\n >\n <MenuItem value={'BAR'}>Bar Chart</MenuItem>\n <MenuItem value={'PIE'}>Pie Chart</MenuItem>\n </Select>\n </StyledFormControl>\n )\n\n if (\n (!queryMetadata && isLoadingQueryMetadata) ||\n !facetToPlot ||\n !columnModel\n ) {\n return (\n <div className=\"SRC-loadingContainer SRC-centerContentColumn\">\n {loadingScreen}\n </div>\n )\n } else {\n return (\n <>\n <ConfirmationDialog\n open={showModal}\n onCancel={() => setShowModal(false)}\n title={plotTitle ?? ''}\n content={<FacetNavPanel {...props} isModalView={true} />}\n hasCancelButton={false}\n confirmButtonProps={{ children: 'Apply Filters' }}\n onConfirm={() => setShowModal(false)}\n maxWidth={'md'}\n />\n <div\n role=\"figure\"\n className={`FacetNavPanel${isModalView ? '--expanded' : ''}`}\n >\n {!isModalView && (\n <PlotPanelHeader\n data={queryMetadata}\n isLoading={isLoadingQueryMetadata}\n title={plotTitle}\n facetToPlot={facetToPlot}\n onHide={onHide}\n setShowModal={setShowModal}\n />\n )}\n {isModalView && (\n <Stack\n sx={{\n gap: 2,\n }}\n >\n <StyledFormControl>\n <InputLabel\n sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}\n >\n <span>Filter All Data By</span>\n <Tooltip title=\"Selecting items in this dropdown will affect all facets on the Explore page.\">\n <InfoOutlined className=\"SRC-hand-cursor SRC-secondary-text-color\" />\n </Tooltip>\n </InputLabel>\n <EnumFacetFilter\n facet={facetToPlot}\n containerAs=\"Dropdown\"\n dropdownType=\"SelectBox\"\n />\n </StyledFormControl>\n {chartSelectionToggle}\n </Stack>\n )}\n <Box\n sx={{\n display: 'grid',\n gridTemplateColumns: '50% 50%',\n alignItems: 'center',\n }}\n role=\"graphics-object\"\n className=\"FacetNavPanel__body\"\n >\n <div ref={plotContainerRef}>\n <Plot\n key={`${facetToPlot.columnName}-${facetToPlot.jsonPath}-${plotType}-${plotContainerMeasurements?.width}`}\n layout={layout}\n data={plotData?.data ?? []}\n style={getPlotStyle(\n plotContainerMeasurements?.width,\n plotType,\n isModalView ? 300 : 150,\n )}\n config={{ displayModeBar: false }}\n onClick={evt =>\n applyFacetFilter(evt, facetToPlot, applyChangesToGraphSlice)\n }\n />\n </div>\n <FacetPlotLegendList\n labels={plotData?.labels}\n colors={plotData?.colors}\n isExpanded={isModalView}\n />\n </Box>\n </div>\n </>\n )\n }\n}\n\nexport default FacetNavPanel\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlotsContainer.js","names":[],"sources":["../../../../src/components/widgets/facet-nav/PlotsContainer.tsx"],"sourcesContent":["import { UniqueFacetIdentifier } from '@/utils'\nimport { facetObjectMatchesDefinition } from '@/utils/functions/queryUtils'\nimport { Box, Button } from '@mui/material'\nimport type { PlotType as PlotlyPlotType } from 'plotly.js-basic-dist'\nimport { Suspense, useEffect, useMemo, useState } from 'react'\nimport { useQueryVisualizationContext } from '../../QueryVisualizationWrapper'\nimport { useSuspenseGetQueryMetadata } from '../../QueryWrapper/useGetQueryMetadata'\nimport QueryWrapperSynapsePlot, {\n QueryWrapperSynapsePlotProps,\n} from '../../QueryWrapperPlotNav/QueryWrapperSynapsePlot'\nimport FacetNavPanel, {\n FacetNavPanelProps,\n PlotType,\n} from '../facet-nav/FacetNavPanel'\nimport { PlotsContainerSkeleton } from './PlotsContainerSkeleton'\nimport useFacetPlots, { getFacets } from './useFacetPlots'\n\nconst DEFAULT_VISIBLE_PLOTS = 2\ntype ShowMoreState = 'MORE' | 'LESS' | 'NONE'\nexport type PlotsContainerProps = {\n facetsToPlot?: string[]\n customPlots?: QueryWrapperSynapsePlotProps[]\n initialPlotTypeByFacetColumnName?: Record<string, PlotType>\n}\ntype CustomPlotIdentifier = {\n title: string\n __custom: true\n}\nexport type PlotIdentifier = UniqueFacetIdentifier | CustomPlotIdentifier\n\nexport type UiPlotState = {\n plotId: PlotIdentifier\n isHidden: boolean\n plotType: PlotType\n index?: number\n}\nconst plotMatchesDefinition = (\n definition: PlotIdentifier,\n plotId: PlotIdentifier,\n) => {\n if ('__custom' in plotId && '__custom' in definition) {\n return definition.title == plotId.title\n } else if (!('__custom' in plotId) && !('__custom' in definition)) {\n return facetObjectMatchesDefinition(definition, plotId)\n } else {\n return false\n }\n}\n\nfunction convertPlotlyPlotTypeToFacetNavPlotType(\n plotlyPlotType: PlotlyPlotType,\n): PlotType {\n if (plotlyPlotType === 'bar') {\n return 'BAR'\n } else if (plotlyPlotType === 'pie') {\n return 'PIE'\n }\n return 'PIE'\n}\n\nconst generatePlotKey = (plotUiState: UiPlotState) => {\n if ('__custom' in plotUiState.plotId) {\n // For custom plots\n return `custom-${plotUiState.plotId.title}`\n } else {\n // For facet plots\n return `facet-${plotUiState.plotId.columnName}-${plotUiState.plotId.jsonPath}`\n }\n}\n\nconst isPlotInState = (\n plotId: PlotIdentifier,\n plotUiStateArray: UiPlotState[],\n): boolean => {\n return !!plotUiStateArray.find(plot => {\n return plotMatchesDefinition(plot.plotId, plotId)\n })\n}\n\nconst getCustomPlotIdentifier = (\n customPlot: QueryWrapperSynapsePlotProps,\n): PlotIdentifier => {\n return { __custom: true, title: customPlot.title ?? '' }\n}\n\nconst getCombinedNewPlots = (\n customPlots: QueryWrapperSynapsePlotProps[] = [],\n facetNavPanelPropsArray: Pick<\n FacetNavPanelProps,\n 'applyChangesToFacetFilter' | 'applyChangesToGraphSlice' | 'facetToPlot'\n >[] = [],\n initialPlotTypeByFacetColumnName?: Record<string, PlotType>,\n): UiPlotState[] => [\n ...customPlots.map((plotProps, index) => ({\n plotId: getCustomPlotIdentifier(plotProps),\n isHidden: index >= DEFAULT_VISIBLE_PLOTS,\n plotType: convertPlotlyPlotTypeToFacetNavPlotType(plotProps.type),\n })),\n ...facetNavPanelPropsArray.map((facetPlotProps, index) => ({\n plotId: facetPlotProps.facetToPlot,\n isHidden: index + customPlots.length >= DEFAULT_VISIBLE_PLOTS,\n plotType:\n initialPlotTypeByFacetColumnName?.[\n facetPlotProps.facetToPlot.columnName\n ] ?? DEFAULT_PLOT_TYPE,\n })),\n]\n\n// Remove plots that are no longer in props from prevPlots\nconst isPlotStillPresent = (\n prevPlot: UiPlotState,\n customPlots: QueryWrapperSynapsePlotProps[],\n facetNavPanelProps: Pick<\n FacetNavPanelProps,\n 'applyChangesToFacetFilter' | 'applyChangesToGraphSlice' | 'facetToPlot'\n >[],\n): boolean => {\n return (\n customPlots.some(customPlot =>\n plotMatchesDefinition(\n getCustomPlotIdentifier(customPlot),\n prevPlot.plotId,\n ),\n ) ||\n facetNavPanelProps.some(facetPlot =>\n plotMatchesDefinition(facetPlot.facetToPlot, prevPlot.plotId),\n )\n )\n} // fn returns true iff the plot id is in customPlots or facetNavPanelPropsArray\n\nconst DEFAULT_PLOT_TYPE: PlotType = 'PIE'\n\nconst DEFAULT_FACETS_TO_PLOT: string[] = []\nconst DEFAULT_CUSTOM_PLOTS: QueryWrapperSynapsePlotProps[] = []\n\nfunction PlotsContainer(props: PlotsContainerProps) {\n const {\n facetsToPlot = DEFAULT_FACETS_TO_PLOT,\n customPlots = DEFAULT_CUSTOM_PLOTS,\n initialPlotTypeByFacetColumnName,\n } = props\n const { data: queryMetadata } = useSuspenseGetQueryMetadata()\n const { showPlots: showPlotVisualization } = useQueryVisualizationContext()\n const [plotUiStateArray, setPlotUiStateArray] = useState<UiPlotState[]>([])\n const facetNavPanelPropsArray = useFacetPlots(facetsToPlot)\n\n const getPlotType = (plotId: PlotIdentifier): PlotType => {\n const plotType = plotUiStateArray.find(item =>\n plotMatchesDefinition(plotId, item.plotId),\n )?.plotType\n return plotType ?? DEFAULT_PLOT_TYPE\n }\n\n useEffect(() => {\n const combinedNewPlots = getCombinedNewPlots(\n customPlots,\n facetNavPanelPropsArray,\n initialPlotTypeByFacetColumnName,\n )\n\n // Update the state with new plots\n setPlotUiStateArray(prevPlots => {\n // Filter to only include new plots\n const newPlots = combinedNewPlots.filter(plot => {\n const inState = isPlotInState(plot.plotId, prevPlots)\n return !inState\n })\n\n const updatedPlots = prevPlots.filter(prevPlot =>\n isPlotStillPresent(prevPlot, customPlots, facetNavPanelPropsArray),\n )\n\n // Append new plots\n const combinedPlots = [...updatedPlots, ...newPlots]\n return combinedPlots\n })\n }, [customPlots, facetNavPanelPropsArray, initialPlotTypeByFacetColumnName])\n\n // when 'show more/less' is clicked\n const onShowMoreClick = (shouldShowMore: boolean) => {\n setPlotUiStateArray(plotUiStateArray => {\n return plotUiStateArray.map((item, index) => {\n if (shouldShowMore) {\n // show everything\n return { ...item, isHidden: false }\n }\n // otherwise hide everything except the first few items\n return { ...item, isHidden: index >= DEFAULT_VISIBLE_PLOTS }\n })\n })\n }\n\n const isPlotHiddenInGrid = (plotId: PlotIdentifier) => {\n const itemHidden = plotUiStateArray.find(\n item =>\n plotMatchesDefinition(plotId, item.plotId) && item.isHidden === true,\n )\n const result = itemHidden !== undefined\n return result\n }\n\n const showMoreButtonState = useMemo<ShowMoreState>(() => {\n if (\n // if at least one item is hidden\n plotUiStateArray.find(item => item.isHidden === true)\n ) {\n return 'MORE'\n } else if (plotUiStateArray.length <= DEFAULT_VISIBLE_PLOTS) {\n return 'NONE'\n } else {\n return 'LESS'\n }\n }, [plotUiStateArray])\n\n // hides plot graph\n const hidePlotInGrid = (plotId: PlotIdentifier) => {\n setUiPropertyForPlot(plotId, 'isHidden', true)\n }\n\n const setPlotType = (plotId: PlotIdentifier, plotType: PlotType) => {\n setUiPropertyForPlot(plotId, 'plotType', plotType)\n }\n\n const setUiPropertyForPlot = (\n plotId: PlotIdentifier,\n propName: keyof UiPlotState,\n value: boolean | PlotType, // 'the possible values of the above type' (currently can't be specified in TS using symbols)\n ) => {\n setPlotUiStateArray(plotUiStateArray =>\n plotUiStateArray.map(item =>\n plotMatchesDefinition(plotId, item.plotId)\n ? { ...item, [propName]: value }\n : item,\n ),\n )\n }\n\n const colorTracker: { facet: PlotIdentifier; colorIndex: number }[] =\n // TODO: customPlots should use the color index\n // additionally, it is unclear why this object is created\n // We can probably just pass the index from `plotUiStateArray.map)\n getFacets(queryMetadata, facetsToPlot).map((el, index) => {\n return {\n facet: { columnName: el.columnName, jsonPath: el.jsonPath },\n colorIndex: index,\n }\n })\n\n return (\n <>\n {plotUiStateArray.length > 0 && (\n <div\n className={`PlotsContainer ${showPlotVisualization ? '' : 'hidden'} ${\n showMoreButtonState === 'LESS' ? 'less' : ''\n }`}\n >\n <div className=\"PlotsContainer__row\" role=\"list\">\n {plotUiStateArray.map(plotUiState => {\n const isCustomPlot = '__custom' in plotUiState.plotId\n const customPlotProps = customPlots.find(customPlot =>\n plotMatchesDefinition(\n getCustomPlotIdentifier(customPlot),\n plotUiState.plotId,\n ),\n )\n const facetNavPanelProps = facetNavPanelPropsArray.find(props =>\n plotMatchesDefinition(props.facetToPlot, plotUiState.plotId),\n )\n\n return (\n <div\n className={\n plotUiState.plotType === 'BAR'\n ? 'PlotsContainer__row__item--full-width'\n : undefined\n }\n style={{\n minWidth: '435px',\n display: isPlotHiddenInGrid(plotUiState.plotId)\n ? 'none'\n : 'block',\n }}\n key={generatePlotKey(plotUiState)}\n >\n {isCustomPlot && customPlotProps && (\n <QueryWrapperSynapsePlot\n {...customPlotProps}\n onHide={() => hidePlotInGrid(plotUiState.plotId)}\n />\n )}\n {!isCustomPlot && facetNavPanelProps && (\n <FacetNavPanel\n index={\n colorTracker.find(el =>\n plotMatchesDefinition(el.facet, plotUiState.plotId),\n )?.colorIndex!\n }\n onHide={() => hidePlotInGrid(plotUiState.plotId)}\n plotType={getPlotType(plotUiState.plotId)}\n onSetPlotType={(plotType: PlotType) =>\n setPlotType(plotUiState.plotId, plotType)\n }\n isModalView={false}\n {...facetNavPanelProps}\n />\n )}\n </div>\n )\n })}\n </div>\n {showMoreButtonState !== 'NONE' && (\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'center',\n backgroundColor: 'grey.100',\n p: 2,\n mt: 2,\n }}\n >\n <Button\n variant=\"contained\"\n color=\"secondary\"\n onClick={() => onShowMoreClick(showMoreButtonState === 'MORE')}\n sx={{ width: '150px' }}\n >\n {showMoreButtonState === 'LESS'\n ? 'Hide Charts'\n : 'View All Charts'}\n </Button>\n </Box>\n )}\n </div>\n )}\n </>\n )\n}\n\nexport default function PlotsContainerWithSuspense(props: PlotsContainerProps) {\n const { showPlots } = useQueryVisualizationContext()\n return (\n <Suspense fallback={showPlots ? <PlotsContainerSkeleton /> : null}>\n <PlotsContainer {...props} />\n </Suspense>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PlotsContainer.js","names":[],"sources":["../../../../src/components/widgets/facet-nav/PlotsContainer.tsx"],"sourcesContent":["import { UniqueFacetIdentifier } from '@/utils'\nimport { facetObjectMatchesDefinition } from '@/utils/functions/queryUtils'\nimport { Box, Button } from '@mui/material'\nimport type { PlotType as PlotlyPlotType } from 'plotly.js-basic-dist'\nimport { Suspense, useEffect, useMemo, useState } from 'react'\nimport { useQueryVisualizationContext } from '../../QueryVisualizationWrapper'\nimport { useSuspenseGetQueryMetadata } from '../../QueryWrapper/useGetQueryMetadata'\nimport QueryWrapperSynapsePlot, {\n QueryWrapperSynapsePlotProps,\n} from '../../QueryWrapperPlotNav/QueryWrapperSynapsePlot'\nimport FacetNavPanel, {\n FacetNavPanelProps,\n PlotType,\n} from '../facet-nav/FacetNavPanel'\nimport { PlotsContainerSkeleton } from './PlotsContainerSkeleton'\nimport useFacetPlots, { getFacets } from './useFacetPlots'\n\nconst DEFAULT_VISIBLE_PLOTS = 2\ntype ShowMoreState = 'MORE' | 'LESS' | 'NONE'\nexport type PlotsContainerProps = {\n facetsToPlot?: string[]\n customPlots?: QueryWrapperSynapsePlotProps[]\n initialPlotTypeByFacetColumnName?: Record<string, PlotType>\n}\ntype CustomPlotIdentifier = {\n title: string\n __custom: true\n}\nexport type PlotIdentifier = UniqueFacetIdentifier | CustomPlotIdentifier\n\nexport type UiPlotState = {\n plotId: PlotIdentifier\n isHidden: boolean\n plotType: PlotType\n index?: number\n}\nconst plotMatchesDefinition = (\n definition: PlotIdentifier,\n plotId: PlotIdentifier,\n) => {\n if ('__custom' in plotId && '__custom' in definition) {\n return definition.title == plotId.title\n } else if (!('__custom' in plotId) && !('__custom' in definition)) {\n return facetObjectMatchesDefinition(definition, plotId)\n } else {\n return false\n }\n}\n\nfunction convertPlotlyPlotTypeToFacetNavPlotType(\n plotlyPlotType: PlotlyPlotType,\n): PlotType {\n if (plotlyPlotType === 'bar') {\n return 'BAR'\n } else if (plotlyPlotType === 'pie') {\n return 'PIE'\n }\n return 'PIE'\n}\n\nconst generatePlotKey = (plotUiState: UiPlotState) => {\n if ('__custom' in plotUiState.plotId) {\n // For custom plots\n return `custom-${plotUiState.plotId.title}`\n } else {\n // For facet plots\n return `facet-${plotUiState.plotId.columnName}-${plotUiState.plotId.jsonPath}`\n }\n}\n\nconst isPlotInState = (\n plotId: PlotIdentifier,\n plotUiStateArray: UiPlotState[],\n): boolean => {\n return !!plotUiStateArray.find(plot => {\n return plotMatchesDefinition(plot.plotId, plotId)\n })\n}\n\nconst getCustomPlotIdentifier = (\n customPlot: QueryWrapperSynapsePlotProps,\n): PlotIdentifier => {\n return { __custom: true, title: customPlot.title ?? '' }\n}\n\nconst getCombinedNewPlots = (\n customPlots: QueryWrapperSynapsePlotProps[] = [],\n facetNavPanelPropsArray: Pick<\n FacetNavPanelProps,\n 'applyChangesToFacetFilter' | 'applyChangesToGraphSlice' | 'facetToPlot'\n >[] = [],\n initialPlotTypeByFacetColumnName?: Record<string, PlotType>,\n): UiPlotState[] => [\n ...customPlots.map((plotProps, index) => ({\n plotId: getCustomPlotIdentifier(plotProps),\n isHidden: index >= DEFAULT_VISIBLE_PLOTS,\n plotType: convertPlotlyPlotTypeToFacetNavPlotType(plotProps.type),\n })),\n ...facetNavPanelPropsArray.map((facetPlotProps, index) => ({\n plotId: facetPlotProps.facetToPlot,\n isHidden: index + customPlots.length >= DEFAULT_VISIBLE_PLOTS,\n plotType:\n initialPlotTypeByFacetColumnName?.[\n facetPlotProps.facetToPlot.columnName\n ] ?? DEFAULT_PLOT_TYPE,\n })),\n]\n\n// Remove plots that are no longer in props from prevPlots\nconst isPlotStillPresent = (\n prevPlot: UiPlotState,\n customPlots: QueryWrapperSynapsePlotProps[],\n facetNavPanelProps: Pick<\n FacetNavPanelProps,\n 'applyChangesToFacetFilter' | 'applyChangesToGraphSlice' | 'facetToPlot'\n >[],\n): boolean => {\n return (\n customPlots.some(customPlot =>\n plotMatchesDefinition(\n getCustomPlotIdentifier(customPlot),\n prevPlot.plotId,\n ),\n ) ||\n facetNavPanelProps.some(facetPlot =>\n plotMatchesDefinition(facetPlot.facetToPlot, prevPlot.plotId),\n )\n )\n} // fn returns true iff the plot id is in customPlots or facetNavPanelPropsArray\n\nconst DEFAULT_PLOT_TYPE: PlotType = 'PIE'\n\nconst DEFAULT_FACETS_TO_PLOT: string[] = []\nconst DEFAULT_CUSTOM_PLOTS: QueryWrapperSynapsePlotProps[] = []\n\nfunction PlotsContainer(props: PlotsContainerProps) {\n const {\n facetsToPlot = DEFAULT_FACETS_TO_PLOT,\n customPlots = DEFAULT_CUSTOM_PLOTS,\n initialPlotTypeByFacetColumnName,\n } = props\n const { data: queryMetadata } = useSuspenseGetQueryMetadata()\n const { showPlots: showPlotVisualization } = useQueryVisualizationContext()\n const [plotUiStateArray, setPlotUiStateArray] = useState<UiPlotState[]>([])\n const facetNavPanelPropsArray = useFacetPlots(facetsToPlot)\n\n const getPlotType = (plotId: PlotIdentifier): PlotType => {\n const plotType = plotUiStateArray.find(item =>\n plotMatchesDefinition(plotId, item.plotId),\n )?.plotType\n return plotType ?? DEFAULT_PLOT_TYPE\n }\n\n useEffect(() => {\n const combinedNewPlots = getCombinedNewPlots(\n customPlots,\n facetNavPanelPropsArray,\n initialPlotTypeByFacetColumnName,\n )\n\n // Update the state with new plots\n setPlotUiStateArray(prevPlots => {\n // Filter to only include new plots\n const newPlots = combinedNewPlots.filter(plot => {\n const inState = isPlotInState(plot.plotId, prevPlots)\n return !inState\n })\n\n const updatedPlots = prevPlots.filter(prevPlot =>\n isPlotStillPresent(prevPlot, customPlots, facetNavPanelPropsArray),\n )\n\n // Append new plots\n const combinedPlots = [...updatedPlots, ...newPlots]\n return combinedPlots\n })\n }, [customPlots, facetNavPanelPropsArray, initialPlotTypeByFacetColumnName])\n\n // when 'show more/less' is clicked\n const onShowMoreClick = (shouldShowMore: boolean) => {\n setPlotUiStateArray(plotUiStateArray => {\n return plotUiStateArray.map((item, index) => {\n if (shouldShowMore) {\n // show everything\n return { ...item, isHidden: false }\n }\n // otherwise hide everything except the first few items\n return { ...item, isHidden: index >= DEFAULT_VISIBLE_PLOTS }\n })\n })\n }\n\n const isPlotHiddenInGrid = (plotId: PlotIdentifier) => {\n const itemHidden = plotUiStateArray.find(\n item =>\n plotMatchesDefinition(plotId, item.plotId) && item.isHidden === true,\n )\n const result = itemHidden !== undefined\n return result\n }\n\n const showMoreButtonState = useMemo<ShowMoreState>(() => {\n if (\n // if at least one item is hidden\n plotUiStateArray.find(item => item.isHidden === true)\n ) {\n return 'MORE'\n } else if (plotUiStateArray.length <= DEFAULT_VISIBLE_PLOTS) {\n return 'NONE'\n } else {\n return 'LESS'\n }\n }, [plotUiStateArray])\n\n // hides plot graph\n const hidePlotInGrid = (plotId: PlotIdentifier) => {\n setUiPropertyForPlot(plotId, 'isHidden', true)\n }\n\n const setPlotType = (plotId: PlotIdentifier, plotType: PlotType) => {\n setUiPropertyForPlot(plotId, 'plotType', plotType)\n }\n\n const setUiPropertyForPlot = (\n plotId: PlotIdentifier,\n propName: keyof UiPlotState,\n value: boolean | PlotType, // 'the possible values of the above type' (currently can't be specified in TS using symbols)\n ) => {\n setPlotUiStateArray(plotUiStateArray =>\n plotUiStateArray.map(item =>\n plotMatchesDefinition(plotId, item.plotId)\n ? { ...item, [propName]: value }\n : item,\n ),\n )\n }\n\n const colorTracker: { facet: PlotIdentifier; colorIndex: number }[] =\n // TODO: customPlots should use the color index\n // additionally, it is unclear why this object is created\n // We can probably just pass the index from `plotUiStateArray.map)\n getFacets(queryMetadata, facetsToPlot).map((el, index) => {\n return {\n facet: { columnName: el.columnName, jsonPath: el.jsonPath },\n colorIndex: index,\n }\n })\n\n return (\n <>\n {plotUiStateArray.length > 0 && (\n <div\n className={`PlotsContainer ${showPlotVisualization ? '' : 'hidden'} ${\n showMoreButtonState === 'LESS' ? 'less' : ''\n }`}\n >\n <div className=\"PlotsContainer__row\" role=\"list\">\n {plotUiStateArray.map(plotUiState => {\n const isCustomPlot = '__custom' in plotUiState.plotId\n const customPlotProps = customPlots.find(customPlot =>\n plotMatchesDefinition(\n getCustomPlotIdentifier(customPlot),\n plotUiState.plotId,\n ),\n )\n const facetNavPanelProps = facetNavPanelPropsArray.find(props =>\n plotMatchesDefinition(props.facetToPlot, plotUiState.plotId),\n )\n\n return (\n <div\n className={\n plotUiState.plotType === 'BAR'\n ? 'PlotsContainer__row__item--full-width'\n : undefined\n }\n style={{\n minWidth: '435px',\n display: isPlotHiddenInGrid(plotUiState.plotId)\n ? 'none'\n : 'block',\n }}\n key={generatePlotKey(plotUiState)}\n >\n {isCustomPlot && customPlotProps && (\n <QueryWrapperSynapsePlot\n {...customPlotProps}\n onHide={() => hidePlotInGrid(plotUiState.plotId)}\n />\n )}\n {!isCustomPlot && facetNavPanelProps && (\n <FacetNavPanel\n index={\n colorTracker.find(el =>\n plotMatchesDefinition(el.facet, plotUiState.plotId),\n )?.colorIndex!\n }\n onHide={() => hidePlotInGrid(plotUiState.plotId)}\n plotType={getPlotType(plotUiState.plotId)}\n onSetPlotType={(plotType: PlotType) =>\n setPlotType(plotUiState.plotId, plotType)\n }\n isModalView={false}\n {...facetNavPanelProps}\n />\n )}\n </div>\n )\n })}\n </div>\n {showMoreButtonState !== 'NONE' && (\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'center',\n backgroundColor: 'grey.100',\n p: 2,\n mt: 2,\n }}\n >\n <Button\n variant=\"contained\"\n color=\"secondary\"\n onClick={() => onShowMoreClick(showMoreButtonState === 'MORE')}\n sx={{ width: '150px' }}\n >\n {showMoreButtonState === 'LESS'\n ? 'Hide Charts'\n : 'View All Charts'}\n </Button>\n </Box>\n )}\n </div>\n )}\n </>\n )\n}\n\nexport default function PlotsContainerWithSuspense(props: PlotsContainerProps) {\n const { showPlots } = useQueryVisualizationContext()\n return (\n <Suspense fallback={showPlots ? <PlotsContainerSkeleton /> : null}>\n <PlotsContainer {...props} />\n </Suspense>\n )\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,IAAM,IAAwB,GAmBxB,KACJ,GACA,MAEI,cAAc,KAAU,cAAc,IACjC,EAAW,SAAS,EAAO,QACzB,EAAE,cAAc,MAAW,EAAE,cAAc,KAC7C,EAA6B,GAAY,EAAO,GAEhD;AAIX,SAAS,EACP,GACU;AAMV,QALI,MAAmB,QACd,QAEA;;AAKX,IAAM,KAAmB,MACnB,cAAc,EAAY,SAErB,UAAU,EAAY,OAAO,UAG7B,SAAS,EAAY,OAAO,WAAW,GAAG,EAAY,OAAO,YAIlE,KACJ,GACA,MAEO,CAAC,CAAC,EAAiB,MAAK,MACtB,EAAsB,EAAK,QAAQ,EAAO,CACjD,EAGE,KACJ,OAEO;CAAE,UAAU;CAAM,OAAO,EAAW,SAAS;CAAI,GAGpD,KACJ,IAA8C,EAAE,EAChD,IAGM,EAAE,EACR,MACkB,CAClB,GAAG,EAAY,KAAK,GAAW,OAAW;CACxC,QAAQ,EAAwB,EAAU;CAC1C,UAAU,KAAS;CACnB,UAAU,EAAwC,EAAU,KAAK;CAClE,EAAE,EACH,GAAG,EAAwB,KAAK,GAAgB,OAAW;CACzD,QAAQ,EAAe;CACvB,UAAU,IAAQ,EAAY,UAAU;CACxC,UACE,IACE,EAAe,YAAY,eACxB;CACR,EAAE,CACJ,EAGK,KACJ,GACA,GACA,MAME,EAAY,MAAK,MACf,EACE,EAAwB,EAAW,EACnC,EAAS,OACV,CACF,IACD,EAAmB,MAAK,MACtB,EAAsB,EAAU,aAAa,EAAS,OAAO,CAC9D,EAIC,IAA8B,OAE9B,IAAmC,EAAE,EACrC,IAAuD,EAAE;AAE/D,SAAS,EAAe,GAA4B;CAClD,IAAM,EACJ,kBAAe,GACf,iBAAc,GACd,wCACE,GACE,EAAE,MAAM,MAAkB,GAA6B,EACvD,EAAE,WAAW,MAA0B,GAA8B,EACrE,CAAC,GAAkB,KAAuB,EAAwB,EAAE,CAAC,EACrE,IAA0B,EAAc,EAAa,EAErD,KAAe,MACF,EAAiB,MAAK,MACrC,EAAsB,GAAQ,EAAK,OAAO,CAC3C,EAAE,YACgB;AAGrB,SAAgB;EACd,IAAM,IAAmB,EACvB,GACA,GACA,EACD;AAGD,KAAoB,MAAa;GAE/B,IAAM,IAAW,EAAiB,QAAO,MAEhC,CADS,EAAc,EAAK,QAAQ,EACnC,CACR;AAQF,UAAO,CADgB,GALF,EAAU,QAAO,MACpC,EAAmB,GAAU,GAAa,EAAwB,CAI1C,EAAc,GAAG,EACpC;IACP;IACD;EAAC;EAAa;EAAyB;EAAiC,CAAC;CAG5E,IAAM,KAAmB,MAA4B;AACnD,KAAoB,MACX,EAAiB,KAAK,GAAM,MAC7B,IAEK;GAAE,GAAG;GAAM,UAAU;GAAO,GAG9B;GAAE,GAAG;GAAM,UAAU,KAAS;GAAuB,CAC5D,CACF;IAGE,KAAsB,MACP,EAAiB,MAClC,MACE,EAAsB,GAAQ,EAAK,OAAO,IAAI,EAAK,aAAa,GAErD,KAAe,KAAA,GAI1B,IAAsB,QAGxB,EAAiB,MAAK,MAAQ,EAAK,aAAa,GAAK,GAE9C,SACE,EAAiB,UAAU,IAC7B,SAEA,QAER,CAAC,EAAiB,CAAC,EAGhB,KAAkB,MAA2B;AACjD,IAAqB,GAAQ,YAAY,GAAK;IAG1C,KAAe,GAAwB,MAAuB;AAClE,IAAqB,GAAQ,YAAY,EAAS;IAG9C,KACJ,GACA,GACA,MACG;AACH,KAAoB,MAClB,EAAiB,KAAI,MACnB,EAAsB,GAAQ,EAAK,OAAO,GACtC;GAAE,GAAG;IAAO,IAAW;GAAO,GAC9B,EACL,CACF;IAGG,IAIJ,EAAU,GAAe,EAAa,CAAC,KAAK,GAAI,OACvC;EACL,OAAO;GAAE,YAAY,EAAG;GAAY,UAAU,EAAG;GAAU;EAC3D,YAAY;EACb,EACD;AAEJ,QACE,kBAAA,GAAA,EAAA,UACG,EAAiB,SAAS,KACzB,kBAAC,OAAD;EACE,WAAW,kBAAkB,IAAwB,KAAK,SAAS,GACjE,MAAwB,SAAS,SAAS;YAF9C,CAKE,kBAAC,OAAD;GAAK,WAAU;GAAsB,MAAK;aACvC,EAAiB,KAAI,MAAe;IACnC,IAAM,IAAe,cAAc,EAAY,QACzC,IAAkB,EAAY,MAAK,MACvC,EACE,EAAwB,EAAW,EACnC,EAAY,OACb,CACF,EACK,IAAqB,EAAwB,MAAK,MACtD,EAAsB,EAAM,aAAa,EAAY,OAAO,CAC7D;AAED,WACE,kBAAC,OAAD;KACE,WACE,EAAY,aAAa,QACrB,0CACA,KAAA;KAEN,OAAO;MACL,UAAU;MACV,SAAS,EAAmB,EAAY,OAAO,GAC3C,SACA;MACL;eAXH,CAcG,KAAgB,KACf,kBAAC,GAAD;MACE,GAAI;MACJ,cAAc,EAAe,EAAY,OAAO;MAChD,CAAA,EAEH,CAAC,KAAgB,KAChB,kBAAC,GAAD;MACE,OACE,EAAa,MAAK,MAChB,EAAsB,EAAG,OAAO,EAAY,OAAO,CACpD,EAAE;MAEL,cAAc,EAAe,EAAY,OAAO;MAChD,UAAU,EAAY,EAAY,OAAO;MACzC,gBAAgB,MACd,EAAY,EAAY,QAAQ,EAAS;MAE3C,aAAa;MACb,GAAI;MACJ,CAAA,CAEA;OAxBC,EAAgB,EAAY,CAwB7B;KAER;GACE,CAAA,EACL,MAAwB,UACvB,kBAAC,GAAD;GACE,IAAI;IACF,SAAS;IACT,gBAAgB;IAChB,iBAAiB;IACjB,GAAG;IACH,IAAI;IACL;aAED,kBAAC,GAAD;IACE,SAAQ;IACR,OAAM;IACN,eAAe,EAAgB,MAAwB,OAAO;IAC9D,IAAI,EAAE,OAAO,SAAS;cAErB,MAAwB,SACrB,gBACA;IACG,CAAA;GACL,CAAA,CAEJ;KAEP,CAAA;;AAIP,SAAwB,EAA2B,GAA4B;CAC7E,IAAM,EAAE,iBAAc,GAA8B;AACpD,QACE,kBAAC,GAAD;EAAU,UAAU,IAAY,kBAAC,GAAD,EAA0B,CAAA,GAAG;YAC3D,kBAAC,GAAD,EAAgB,GAAI,GAAS,CAAA;EACpB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectionCriteriaPills.js","names":[],"sources":["../../../../src/components/widgets/facet-nav/SelectionCriteriaPills.tsx"],"sourcesContent":["import {\n isColumnMultiValueFunctionQueryFilter,\n isColumnSingleValueQueryFilter,\n isFacetColumnRangeRequest,\n isFacetColumnValuesRequest,\n isTextMatchesQueryFilter,\n LockedColumn,\n} from '@/utils'\nimport { FRIENDLY_VALUE_NOT_SET, VALUE_NOT_SET } from '@/utils/SynapseConstants'\nimport {\n ColumnModel,\n ColumnMultiValueFunctionQueryFilter,\n ColumnSingleValueQueryFilter,\n FacetColumnRangeRequest,\n FacetColumnRequest,\n QueryFilter,\n TextMatchesQueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport pluralize from 'pluralize'\nimport { ReadonlyDeep } from 'type-fest'\nimport { QueryContextType, useQueryContext } from '../../QueryContext'\nimport {\n QueryVisualizationContextType,\n useQueryVisualizationContext,\n} from '../../QueryVisualizationWrapper'\nimport { useGetQueryMetadata } from '../../QueryWrapper/useGetQueryMetadata'\nimport SelectionCriteriaPill, {\n SelectionCriteriaPillProps,\n} from './SelectionCriteriaPill'\nimport { getSearchTextFromBooleanModeSearchExpression } from '@/components/FullTextSearch/FullTextSearchUtils'\n\nconst MAX_VALUES_IN_FILTER_FOR_INDIVIDUAL_PILLS = 4\n\nfunction getPillPropsFromColumnQueryFilter(\n queryFilter:\n | ColumnSingleValueQueryFilter\n | ColumnMultiValueFunctionQueryFilter,\n queryContext: QueryContextType,\n columnModel: ColumnModel | undefined,\n queryVisualizationContext: QueryVisualizationContextType,\n): SelectionCriteriaPillProps[] {\n const { getColumnDisplayName } = queryVisualizationContext\n // ColumnSingleValueQueryFilter and ColumnMultiValueQueryFilter both allow for a list of values\n // If there are more than _n_ values, consolidate to one pill\n if (\n queryFilter.values.length > MAX_VALUES_IN_FILTER_FOR_INDIVIDUAL_PILLS ||\n !columnModel\n ) {\n const text = `${pluralize(\n getColumnDisplayName(queryFilter.columnName),\n )} (${queryFilter.values.length.toLocaleString()})`\n\n return [\n {\n key: `queryFilter-${queryFilter.concreteType}-${queryFilter.columnName}`,\n innerText: text,\n tooltipText: text,\n onRemoveFilter: () => {\n queryContext.removeQueryFilter(queryFilter)\n },\n },\n ]\n }\n\n // otherwise render one pill per value\n return queryFilter.values.map(value => {\n let filterValue = value\n\n if (value?.startsWith('%') && value?.endsWith('%')) {\n // strip '%' wildcard character when using a LIKE condition\n filterValue = filterValue.substring(1, filterValue.length - 1)\n }\n filterValue = queryVisualizationContext.getDisplayValue(\n filterValue,\n columnModel.columnType,\n )\n const text = `${getColumnDisplayName(\n queryFilter.columnName,\n )}: ${filterValue}`\n return {\n key: `queryFilter-${queryFilter.concreteType}-${queryFilter.columnName}-${value}`,\n innerText: text,\n tooltipText: text,\n onRemoveFilter: () => {\n queryContext.removeValueFromQueryFilter(queryFilter, value)\n },\n }\n })\n}\n\nexport function getPillPropsFromTextMatchesQueryFilter(\n queryFilter: TextMatchesQueryFilter,\n queryContext: QueryContextType,\n): SelectionCriteriaPillProps {\n let innerText = queryFilter.searchExpression\n if (queryFilter.searchMode == 'BOOLEAN') {\n innerText = getSearchTextFromBooleanModeSearchExpression(\n queryFilter.searchExpression,\n )\n }\n\n return {\n key: `queryFilter-${queryFilter.concreteType}-${queryFilter.searchExpression}`,\n innerText,\n tooltipText: `Text matches: \"${innerText}\"`,\n onRemoveFilter: () => {\n queryContext.removeQueryFilter(queryFilter)\n },\n }\n}\n\nfunction getPillPropsFromQueryFilters(\n queryFilters: ReadonlyDeep<QueryFilter[]>,\n queryContext: QueryContextType,\n columnModels: ColumnModel[],\n queryVisualizationContext: QueryVisualizationContextType,\n lockedColumn?: LockedColumn,\n): SelectionCriteriaPillProps[] {\n return queryFilters.flatMap(queryFilter => {\n if (\n isColumnSingleValueQueryFilter(queryFilter) ||\n isColumnMultiValueFunctionQueryFilter(queryFilter)\n ) {\n const columnModel = columnModels.find(\n cm => cm.name === queryFilter.columnName,\n )\n if (\n queryFilter.columnName.toLowerCase() ===\n lockedColumn?.columnName?.toLowerCase()\n ) {\n return []\n }\n return getPillPropsFromColumnQueryFilter(\n queryFilter,\n queryContext,\n columnModel,\n queryVisualizationContext,\n )\n } else if (isTextMatchesQueryFilter(queryFilter)) {\n return [getPillPropsFromTextMatchesQueryFilter(queryFilter, queryContext)]\n } else {\n console.log('Unknown query filter type', queryFilter)\n return []\n }\n })\n}\n\nfunction getRangeFacetInnerText(min?: string, max?: string) {\n if (min == undefined && max == undefined) {\n return 'Any value'\n } else if (min == undefined) {\n return `Up to ${max}`\n } else if (max == undefined) {\n return `${min} or greater`\n } else if (min === VALUE_NOT_SET && max === VALUE_NOT_SET) {\n return FRIENDLY_VALUE_NOT_SET\n } else {\n return `${min} - ${max}`\n }\n}\n\nfunction getPillPropsFromFacetFilters(\n selectedFacets: ReadonlyDeep<FacetColumnRequest[]>,\n queryContext: QueryContextType,\n columnModels: ColumnModel[],\n queryVisualizationContext: QueryVisualizationContextType,\n lockedColumn?: LockedColumn,\n): SelectionCriteriaPillProps[] {\n return selectedFacets.flatMap(selectedFacet => {\n if (\n selectedFacet.columnName.toLowerCase() ===\n lockedColumn?.columnName?.toLowerCase()\n ) {\n return []\n }\n const columnModel = columnModels.find(\n cm => cm.name === selectedFacet.columnName,\n )\n const { getColumnDisplayName, getDisplayValue } = queryVisualizationContext\n if (isFacetColumnValuesRequest(selectedFacet)) {\n // If there are more than _n_ values, consolidate to one pill\n if (\n selectedFacet.facetValues.length >\n MAX_VALUES_IN_FILTER_FOR_INDIVIDUAL_PILLS ||\n !columnModel\n ) {\n const text = `${pluralize(\n getColumnDisplayName(\n selectedFacet.columnName,\n selectedFacet.jsonPath,\n ),\n )} (${selectedFacet.facetValues.length.toLocaleString()})`\n return [\n {\n key: `facet-${selectedFacet.concreteType}-${selectedFacet.columnName}`,\n innerText: text,\n tooltipText: text,\n onRemoveFilter: () => {\n queryContext.removeSelectedFacet(selectedFacet)\n },\n },\n ]\n }\n\n // otherwise render one pill per value\n\n return selectedFacet.facetValues.map(facetValue => {\n const innerText = getDisplayValue(facetValue, columnModel.columnType)\n return {\n key: `facet-${selectedFacet.concreteType}-${selectedFacet.columnName}-${facetValue}`,\n innerText: innerText,\n tooltipText: `${getColumnDisplayName(\n selectedFacet.columnName,\n selectedFacet.jsonPath,\n )}: ${innerText}`,\n onRemoveFilter: () => {\n queryContext.removeValueFromSelectedFacet(selectedFacet, facetValue)\n },\n }\n })\n } else if (isFacetColumnRangeRequest(selectedFacet)) {\n // Include a single pill for both facet filters if a combined range facet filter config is defined\n const { combineRangeFacetConfig } = queryContext\n if (\n combineRangeFacetConfig &&\n (selectedFacet.columnName == combineRangeFacetConfig.minFacetColumn ||\n selectedFacet.columnName == combineRangeFacetConfig.maxFacetColumn)\n ) {\n if (\n selectedFacet.columnName == combineRangeFacetConfig.minFacetColumn\n ) {\n return []\n } else {\n // find the min facet also\n const maxFacet = selectedFacet\n const minFacet = selectedFacets.find(\n v => v.columnName == combineRangeFacetConfig.minFacetColumn,\n ) as FacetColumnRangeRequest\n const innerText = getRangeFacetInnerText(maxFacet.min, minFacet.max)\n return [\n {\n key: `facet-${selectedFacet.concreteType}-${selectedFacet.columnName}-${innerText}`,\n innerText: innerText,\n tooltipText: `${combineRangeFacetConfig.label}: ${innerText}`,\n onRemoveFilter: () => {\n // Remove both facets on pill click\n queryContext.removeSelectedFacet([minFacet, maxFacet])\n },\n },\n ]\n }\n }\n\n const innerText = getRangeFacetInnerText(\n selectedFacet.min,\n selectedFacet.max,\n )\n\n return [\n {\n key: `facet-${selectedFacet.concreteType}-${selectedFacet.columnName}-${selectedFacet.min}-${selectedFacet.max}`,\n innerText: innerText,\n tooltipText: `${getColumnDisplayName(\n selectedFacet.columnName,\n selectedFacet.jsonPath,\n )}: ${innerText}`,\n onRemoveFilter: () => {\n queryContext.removeSelectedFacet(selectedFacet)\n },\n },\n ]\n } else {\n console.log(\n 'Unknown facet type',\n (selectedFacet as unknown as FacetColumnRequest).concreteType,\n )\n return []\n }\n })\n}\n\nfunction SelectionCriteriaPills() {\n const queryContext = useQueryContext()\n const lockedColumn = queryContext.lockedColumn\n const queryVisualizationContext = useQueryVisualizationContext()\n const { currentQueryRequest } = queryContext\n const { data: queryMetadata } = useGetQueryMetadata()\n\n const queryFilterPillProps = getPillPropsFromQueryFilters(\n currentQueryRequest.query?.additionalFilters ?? [],\n queryContext,\n queryMetadata?.columnModels || [],\n queryVisualizationContext,\n lockedColumn,\n )\n\n const facetPillProps = getPillPropsFromFacetFilters(\n currentQueryRequest.query.selectedFacets ?? [],\n queryContext,\n queryMetadata?.columnModels || [],\n queryVisualizationContext,\n lockedColumn,\n )\n\n const allPills = [...queryFilterPillProps, ...facetPillProps]\n\n return (\n <>\n {allPills.map(pillProps => {\n // Encode the key because the facet may include an illegal character\n const key = encodeURIComponent(pillProps.key)\n return <SelectionCriteriaPill {...pillProps} key={key} />\n })}\n </>\n )\n}\n\nexport default SelectionCriteriaPills\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SelectionCriteriaPills.js","names":[],"sources":["../../../../src/components/widgets/facet-nav/SelectionCriteriaPills.tsx"],"sourcesContent":["import {\n isColumnMultiValueFunctionQueryFilter,\n isColumnSingleValueQueryFilter,\n isFacetColumnRangeRequest,\n isFacetColumnValuesRequest,\n isTextMatchesQueryFilter,\n LockedColumn,\n} from '@/utils'\nimport { FRIENDLY_VALUE_NOT_SET, VALUE_NOT_SET } from '@/utils/SynapseConstants'\nimport {\n ColumnModel,\n ColumnMultiValueFunctionQueryFilter,\n ColumnSingleValueQueryFilter,\n FacetColumnRangeRequest,\n FacetColumnRequest,\n QueryFilter,\n TextMatchesQueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport pluralize from 'pluralize'\nimport { ReadonlyDeep } from 'type-fest'\nimport { QueryContextType, useQueryContext } from '../../QueryContext'\nimport {\n QueryVisualizationContextType,\n useQueryVisualizationContext,\n} from '../../QueryVisualizationWrapper'\nimport { useGetQueryMetadata } from '../../QueryWrapper/useGetQueryMetadata'\nimport SelectionCriteriaPill, {\n SelectionCriteriaPillProps,\n} from './SelectionCriteriaPill'\nimport { getSearchTextFromBooleanModeSearchExpression } from '@/components/FullTextSearch/FullTextSearchUtils'\n\nconst MAX_VALUES_IN_FILTER_FOR_INDIVIDUAL_PILLS = 4\n\nfunction getPillPropsFromColumnQueryFilter(\n queryFilter:\n | ColumnSingleValueQueryFilter\n | ColumnMultiValueFunctionQueryFilter,\n queryContext: QueryContextType,\n columnModel: ColumnModel | undefined,\n queryVisualizationContext: QueryVisualizationContextType,\n): SelectionCriteriaPillProps[] {\n const { getColumnDisplayName } = queryVisualizationContext\n // ColumnSingleValueQueryFilter and ColumnMultiValueQueryFilter both allow for a list of values\n // If there are more than _n_ values, consolidate to one pill\n if (\n queryFilter.values.length > MAX_VALUES_IN_FILTER_FOR_INDIVIDUAL_PILLS ||\n !columnModel\n ) {\n const text = `${pluralize(\n getColumnDisplayName(queryFilter.columnName),\n )} (${queryFilter.values.length.toLocaleString()})`\n\n return [\n {\n key: `queryFilter-${queryFilter.concreteType}-${queryFilter.columnName}`,\n innerText: text,\n tooltipText: text,\n onRemoveFilter: () => {\n queryContext.removeQueryFilter(queryFilter)\n },\n },\n ]\n }\n\n // otherwise render one pill per value\n return queryFilter.values.map(value => {\n let filterValue = value\n\n if (value?.startsWith('%') && value?.endsWith('%')) {\n // strip '%' wildcard character when using a LIKE condition\n filterValue = filterValue.substring(1, filterValue.length - 1)\n }\n filterValue = queryVisualizationContext.getDisplayValue(\n filterValue,\n columnModel.columnType,\n )\n const text = `${getColumnDisplayName(\n queryFilter.columnName,\n )}: ${filterValue}`\n return {\n key: `queryFilter-${queryFilter.concreteType}-${queryFilter.columnName}-${value}`,\n innerText: text,\n tooltipText: text,\n onRemoveFilter: () => {\n queryContext.removeValueFromQueryFilter(queryFilter, value)\n },\n }\n })\n}\n\nexport function getPillPropsFromTextMatchesQueryFilter(\n queryFilter: TextMatchesQueryFilter,\n queryContext: QueryContextType,\n): SelectionCriteriaPillProps {\n let innerText = queryFilter.searchExpression\n if (queryFilter.searchMode == 'BOOLEAN') {\n innerText = getSearchTextFromBooleanModeSearchExpression(\n queryFilter.searchExpression,\n )\n }\n\n return {\n key: `queryFilter-${queryFilter.concreteType}-${queryFilter.searchExpression}`,\n innerText,\n tooltipText: `Text matches: \"${innerText}\"`,\n onRemoveFilter: () => {\n queryContext.removeQueryFilter(queryFilter)\n },\n }\n}\n\nfunction getPillPropsFromQueryFilters(\n queryFilters: ReadonlyDeep<QueryFilter[]>,\n queryContext: QueryContextType,\n columnModels: ColumnModel[],\n queryVisualizationContext: QueryVisualizationContextType,\n lockedColumn?: LockedColumn,\n): SelectionCriteriaPillProps[] {\n return queryFilters.flatMap(queryFilter => {\n if (\n isColumnSingleValueQueryFilter(queryFilter) ||\n isColumnMultiValueFunctionQueryFilter(queryFilter)\n ) {\n const columnModel = columnModels.find(\n cm => cm.name === queryFilter.columnName,\n )\n if (\n queryFilter.columnName.toLowerCase() ===\n lockedColumn?.columnName?.toLowerCase()\n ) {\n return []\n }\n return getPillPropsFromColumnQueryFilter(\n queryFilter,\n queryContext,\n columnModel,\n queryVisualizationContext,\n )\n } else if (isTextMatchesQueryFilter(queryFilter)) {\n return [getPillPropsFromTextMatchesQueryFilter(queryFilter, queryContext)]\n } else {\n console.log('Unknown query filter type', queryFilter)\n return []\n }\n })\n}\n\nfunction getRangeFacetInnerText(min?: string, max?: string) {\n if (min == undefined && max == undefined) {\n return 'Any value'\n } else if (min == undefined) {\n return `Up to ${max}`\n } else if (max == undefined) {\n return `${min} or greater`\n } else if (min === VALUE_NOT_SET && max === VALUE_NOT_SET) {\n return FRIENDLY_VALUE_NOT_SET\n } else {\n return `${min} - ${max}`\n }\n}\n\nfunction getPillPropsFromFacetFilters(\n selectedFacets: ReadonlyDeep<FacetColumnRequest[]>,\n queryContext: QueryContextType,\n columnModels: ColumnModel[],\n queryVisualizationContext: QueryVisualizationContextType,\n lockedColumn?: LockedColumn,\n): SelectionCriteriaPillProps[] {\n return selectedFacets.flatMap(selectedFacet => {\n if (\n selectedFacet.columnName.toLowerCase() ===\n lockedColumn?.columnName?.toLowerCase()\n ) {\n return []\n }\n const columnModel = columnModels.find(\n cm => cm.name === selectedFacet.columnName,\n )\n const { getColumnDisplayName, getDisplayValue } = queryVisualizationContext\n if (isFacetColumnValuesRequest(selectedFacet)) {\n // If there are more than _n_ values, consolidate to one pill\n if (\n selectedFacet.facetValues.length >\n MAX_VALUES_IN_FILTER_FOR_INDIVIDUAL_PILLS ||\n !columnModel\n ) {\n const text = `${pluralize(\n getColumnDisplayName(\n selectedFacet.columnName,\n selectedFacet.jsonPath,\n ),\n )} (${selectedFacet.facetValues.length.toLocaleString()})`\n return [\n {\n key: `facet-${selectedFacet.concreteType}-${selectedFacet.columnName}`,\n innerText: text,\n tooltipText: text,\n onRemoveFilter: () => {\n queryContext.removeSelectedFacet(selectedFacet)\n },\n },\n ]\n }\n\n // otherwise render one pill per value\n\n return selectedFacet.facetValues.map(facetValue => {\n const innerText = getDisplayValue(facetValue, columnModel.columnType)\n return {\n key: `facet-${selectedFacet.concreteType}-${selectedFacet.columnName}-${facetValue}`,\n innerText: innerText,\n tooltipText: `${getColumnDisplayName(\n selectedFacet.columnName,\n selectedFacet.jsonPath,\n )}: ${innerText}`,\n onRemoveFilter: () => {\n queryContext.removeValueFromSelectedFacet(selectedFacet, facetValue)\n },\n }\n })\n } else if (isFacetColumnRangeRequest(selectedFacet)) {\n // Include a single pill for both facet filters if a combined range facet filter config is defined\n const { combineRangeFacetConfig } = queryContext\n if (\n combineRangeFacetConfig &&\n (selectedFacet.columnName == combineRangeFacetConfig.minFacetColumn ||\n selectedFacet.columnName == combineRangeFacetConfig.maxFacetColumn)\n ) {\n if (\n selectedFacet.columnName == combineRangeFacetConfig.minFacetColumn\n ) {\n return []\n } else {\n // find the min facet also\n const maxFacet = selectedFacet\n const minFacet = selectedFacets.find(\n v => v.columnName == combineRangeFacetConfig.minFacetColumn,\n ) as FacetColumnRangeRequest\n const innerText = getRangeFacetInnerText(maxFacet.min, minFacet.max)\n return [\n {\n key: `facet-${selectedFacet.concreteType}-${selectedFacet.columnName}-${innerText}`,\n innerText: innerText,\n tooltipText: `${combineRangeFacetConfig.label}: ${innerText}`,\n onRemoveFilter: () => {\n // Remove both facets on pill click\n queryContext.removeSelectedFacet([minFacet, maxFacet])\n },\n },\n ]\n }\n }\n\n const innerText = getRangeFacetInnerText(\n selectedFacet.min,\n selectedFacet.max,\n )\n\n return [\n {\n key: `facet-${selectedFacet.concreteType}-${selectedFacet.columnName}-${selectedFacet.min}-${selectedFacet.max}`,\n innerText: innerText,\n tooltipText: `${getColumnDisplayName(\n selectedFacet.columnName,\n selectedFacet.jsonPath,\n )}: ${innerText}`,\n onRemoveFilter: () => {\n queryContext.removeSelectedFacet(selectedFacet)\n },\n },\n ]\n } else {\n console.log(\n 'Unknown facet type',\n (selectedFacet as unknown as FacetColumnRequest).concreteType,\n )\n return []\n }\n })\n}\n\nfunction SelectionCriteriaPills() {\n const queryContext = useQueryContext()\n const lockedColumn = queryContext.lockedColumn\n const queryVisualizationContext = useQueryVisualizationContext()\n const { currentQueryRequest } = queryContext\n const { data: queryMetadata } = useGetQueryMetadata()\n\n const queryFilterPillProps = getPillPropsFromQueryFilters(\n currentQueryRequest.query?.additionalFilters ?? [],\n queryContext,\n queryMetadata?.columnModels || [],\n queryVisualizationContext,\n lockedColumn,\n )\n\n const facetPillProps = getPillPropsFromFacetFilters(\n currentQueryRequest.query.selectedFacets ?? [],\n queryContext,\n queryMetadata?.columnModels || [],\n queryVisualizationContext,\n lockedColumn,\n )\n\n const allPills = [...queryFilterPillProps, ...facetPillProps]\n\n return (\n <>\n {allPills.map(pillProps => {\n // Encode the key because the facet may include an illegal character\n const key = encodeURIComponent(pillProps.key)\n return <SelectionCriteriaPill {...pillProps} key={key} />\n })}\n </>\n )\n}\n\nexport default SelectionCriteriaPills\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFacetPlots.js","names":[],"sources":["../../../../src/components/widgets/facet-nav/useFacetPlots.ts"],"sourcesContent":["import { isSingleNotSetValue } from '@/utils/functions/queryUtils'\nimport {\n FacetColumnRequest,\n FacetColumnResult,\n FacetColumnResultValueCount,\n FacetColumnResultValues,\n QueryResultBundle,\n} from '@sage-bionetworks/synapse-types'\nimport { useCallback, useMemo } from 'react'\nimport { useQueryContext } from '../../QueryContext'\nimport { useSuspenseGetQueryMetadata } from '../../QueryWrapper/useGetQueryMetadata'\nimport { applyChangesToValuesColumn } from '../query-filter/FacetFilterControls'\nimport { FacetNavPanelProps } from './FacetNavPanel'\n\n// Custom hook for generating properties for FacetNavPanel components with filter controls based on the given facets\nexport default function useFacetPlots(\n facetsToPlot: string[],\n): Pick<\n FacetNavPanelProps,\n 'applyChangesToFacetFilter' | 'applyChangesToGraphSlice' | 'facetToPlot'\n>[] {\n const { getCurrentQueryRequest, executeQueryRequest } = useQueryContext()\n\n const { data: queryMetadata } = useSuspenseGetQueryMetadata()\n\n const lastQueryRequest = useMemo(\n () => getCurrentQueryRequest(),\n [getCurrentQueryRequest],\n )\n const facets = useMemo(\n () => getFacets(queryMetadata, facetsToPlot),\n [queryMetadata, facetsToPlot],\n )\n\n const applyChangesFromQueryFilter = useCallback(\n (facets: FacetColumnRequest[]) => {\n executeQueryRequest(\n lastQueryRequest => {\n lastQueryRequest.query.selectedFacets = facets\n lastQueryRequest.query.offset = 0\n return lastQueryRequest\n },\n { debounce: true },\n )\n },\n [executeQueryRequest],\n )\n\n const facetNavPanelProps = useMemo(\n () =>\n facets.map(facet => {\n return {\n facetToPlot: facet as FacetColumnResultValues,\n\n applyChangesToFacetFilter: applyChangesFromQueryFilter,\n\n applyChangesToGraphSlice: (\n facet: FacetColumnResultValues,\n value: FacetColumnResultValueCount | undefined,\n isSelected: boolean,\n ) =>\n applyChangesToValuesColumn(\n lastQueryRequest,\n facet,\n applyChangesFromQueryFilter,\n value?.value,\n isSelected,\n ),\n }\n }),\n [facets, applyChangesFromQueryFilter, lastQueryRequest],\n )\n return facetNavPanelProps\n}\n\nexport function getFacets(\n data: QueryResultBundle | undefined,\n facetsToPlot?: string[],\n): FacetColumnResult[] {\n const result =\n data?.facets?.filter(item => {\n const isFacetToPlot =\n item.facetType === 'enumeration' &&\n (!facetsToPlot?.length || facetsToPlot.indexOf(item.columnName) > -1)\n // PORTALS-1993: only plot if the facet has count data\n return (\n isFacetToPlot &&\n item.facetValues.length > 0 &&\n !isSingleNotSetValue(item)\n )\n }) ?? []\n if (facetsToPlot?.length) {\n result.sort(\n (a, b) =>\n facetsToPlot.indexOf(a.columnName) - facetsToPlot.indexOf(b.columnName),\n )\n }\n return result\n}\n"],"mappings":";;;;;;;AAeA,SAAwB,EACtB,GAIE;CACF,IAAM,EAAE,2BAAwB,2BAAwB,GAAiB,EAEnE,EAAE,MAAM,MAAkB,GAA6B,EAEvD,IAAmB,QACjB,GAAwB,EAC9B,CAAC,EAAuB,CACzB,EACK,IAAS,QACP,EAAU,GAAe,EAAa,EAC5C,CAAC,GAAe,EAAa,CAC9B,EAEK,IAA8B,GACjC,MAAiC;AAChC,KACE,OACE,EAAiB,MAAM,iBAAiB,GACxC,EAAiB,MAAM,SAAS,GACzB,IAET,EAAE,UAAU,IAAM,CACnB;IAEH,CAAC,EAAoB,CACtB;AA0BD,QAxB2B,QAEvB,EAAO,KAAI,OACF;EACL,aAAa;EAEb,2BAA2B;EAE3B,2BACE,GACA,GACA,MAEA,EACE,GACA,GACA,GACA,GAAO,OACP,EACD;EACJ,EACD,EACJ;EAAC;EAAQ;EAA6B;EAAiB,
|
|
1
|
+
{"version":3,"file":"useFacetPlots.js","names":[],"sources":["../../../../src/components/widgets/facet-nav/useFacetPlots.ts"],"sourcesContent":["import { isSingleNotSetValue } from '@/utils/functions/queryUtils'\nimport {\n FacetColumnRequest,\n FacetColumnResult,\n FacetColumnResultValueCount,\n FacetColumnResultValues,\n QueryResultBundle,\n} from '@sage-bionetworks/synapse-types'\nimport { useCallback, useMemo } from 'react'\nimport { useQueryContext } from '../../QueryContext'\nimport { useSuspenseGetQueryMetadata } from '../../QueryWrapper/useGetQueryMetadata'\nimport { applyChangesToValuesColumn } from '../query-filter/FacetFilterControls'\nimport { FacetNavPanelProps } from './FacetNavPanel'\n\n// Custom hook for generating properties for FacetNavPanel components with filter controls based on the given facets\nexport default function useFacetPlots(\n facetsToPlot: string[],\n): Pick<\n FacetNavPanelProps,\n 'applyChangesToFacetFilter' | 'applyChangesToGraphSlice' | 'facetToPlot'\n>[] {\n const { getCurrentQueryRequest, executeQueryRequest } = useQueryContext()\n\n const { data: queryMetadata } = useSuspenseGetQueryMetadata()\n\n const lastQueryRequest = useMemo(\n () => getCurrentQueryRequest(),\n [getCurrentQueryRequest],\n )\n const facets = useMemo(\n () => getFacets(queryMetadata, facetsToPlot),\n [queryMetadata, facetsToPlot],\n )\n\n const applyChangesFromQueryFilter = useCallback(\n (facets: FacetColumnRequest[]) => {\n executeQueryRequest(\n lastQueryRequest => {\n lastQueryRequest.query.selectedFacets = facets\n lastQueryRequest.query.offset = 0\n return lastQueryRequest\n },\n { debounce: true },\n )\n },\n [executeQueryRequest],\n )\n\n const facetNavPanelProps = useMemo(\n () =>\n facets.map(facet => {\n return {\n facetToPlot: facet as FacetColumnResultValues,\n\n applyChangesToFacetFilter: applyChangesFromQueryFilter,\n\n applyChangesToGraphSlice: (\n facet: FacetColumnResultValues,\n value: FacetColumnResultValueCount | undefined,\n isSelected: boolean,\n ) =>\n applyChangesToValuesColumn(\n lastQueryRequest,\n facet,\n applyChangesFromQueryFilter,\n value?.value,\n isSelected,\n ),\n }\n }),\n [facets, applyChangesFromQueryFilter, lastQueryRequest],\n )\n return facetNavPanelProps\n}\n\nexport function getFacets(\n data: QueryResultBundle | undefined,\n facetsToPlot?: string[],\n): FacetColumnResult[] {\n const result =\n data?.facets?.filter(item => {\n const isFacetToPlot =\n item.facetType === 'enumeration' &&\n (!facetsToPlot?.length || facetsToPlot.indexOf(item.columnName) > -1)\n // PORTALS-1993: only plot if the facet has count data\n return (\n isFacetToPlot &&\n item.facetValues.length > 0 &&\n !isSingleNotSetValue(item)\n )\n }) ?? []\n if (facetsToPlot?.length) {\n result.sort(\n (a, b) =>\n facetsToPlot.indexOf(a.columnName) - facetsToPlot.indexOf(b.columnName),\n )\n }\n return result\n}\n"],"mappings":";;;;;;;AAeA,SAAwB,EACtB,GAIE;CACF,IAAM,EAAE,2BAAwB,2BAAwB,GAAiB,EAEnE,EAAE,MAAM,MAAkB,GAA6B,EAEvD,IAAmB,QACjB,GAAwB,EAC9B,CAAC,EAAuB,CACzB,EACK,IAAS,QACP,EAAU,GAAe,EAAa,EAC5C,CAAC,GAAe,EAAa,CAC9B,EAEK,IAA8B,GACjC,MAAiC;AAChC,KACE,OACE,EAAiB,MAAM,iBAAiB,GACxC,EAAiB,MAAM,SAAS,GACzB,IAET,EAAE,UAAU,IAAM,CACnB;IAEH,CAAC,EAAoB,CACtB;AA0BD,QAxB2B,QAEvB,EAAO,KAAI,OACF;EACL,aAAa;EAEb,2BAA2B;EAE3B,2BACE,GACA,GACA,MAEA,EACE,GACA,GACA,GACA,GAAO,OACP,EACD;EACJ,EACD,EACJ;EAAC;EAAQ;EAA6B;EAAiB,CAElD;;AAGT,SAAgB,EACd,GACA,GACqB;CACrB,IAAM,IACJ,GAAM,QAAQ,QAAO,MAEjB,EAAK,cAAc,kBAClB,CAAC,GAAc,UAAU,EAAa,QAAQ,EAAK,WAAW,GAAG,OAIlE,EAAK,YAAY,SAAS,KAC1B,CAAC,EAAoB,EAAK,CAE5B,IAAI,EAAE;AAOV,QANI,GAAc,UAChB,EAAO,MACJ,GAAG,MACF,EAAa,QAAQ,EAAE,WAAW,GAAG,EAAa,QAAQ,EAAE,WAAW,CAC1E,EAEI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombinedRangeFacetFilter.js","names":[],"sources":["../../../../src/components/widgets/query-filter/CombinedRangeFacetFilter.tsx"],"sourcesContent":["import { useGetQueryResultBundleWithAsyncStatus } from '@/synapse-queries'\nimport { SynapseConstants } from '@/utils'\nimport { VALUE_NOT_SET } from '@/utils/SynapseConstants'\nimport { Skeleton } from '@mui/material'\nimport {\n ColumnType,\n FacetColumnResultRange,\n} from '@sage-bionetworks/synapse-types'\nimport { isNumber } from 'lodash-es'\nimport { useMemo } from 'react'\nimport { useQueryContext } from '../../QueryContext'\nimport { useQueryVisualizationContext } from '../../QueryVisualizationWrapper'\nimport { RangeValues } from '../Range'\nimport { RangeFacetFilterUI } from './RangeFacetFilterUI'\n\nexport type CombinedRangeFacetFilterProps = {\n facetResults: FacetColumnResultRange[]\n label: string\n columnType: ColumnType\n}\n\n/**\n * Inclusive range selector across two columns\n * Written for the ELITE portal cohort builder, may have other uses.\n * The following diagram shows how the Range Selector min and max values are used\n * to define the facet range values:\n * \n\n Range Selector \n min ├────────────────────────┤ max\n \n Min Column facet \n Column Min value ◄────────────────────────────┤ Range Selector max\n \n Max Column facet\n Range Selector min ├────────────────────────────► Column Max value\n \n\n*/\nexport function CombinedRangeFacetFilter({\n facetResults,\n label,\n columnType,\n}: CombinedRangeFacetFilterProps) {\n const { getCurrentQueryRequest, setRangeFacetValue, removeSelectedFacet } =\n useQueryContext()\n const { getColumnDisplayName } = useQueryVisualizationContext()\n\n const {\n columnName: col1Name,\n // columnMax: col1Max // not used\n // selectedMin: col1SelectedMin // not used\n selectedMax: col1SelectedMax,\n } = facetResults[0]\n const {\n columnName: col2Name,\n //columnMin: col2Min, // not used\n selectedMin: col2SelectedMin,\n // selectedMax: col2SelectedMax, // not used\n } = facetResults[1]\n\n // Also query for the facet columns full range (without any selected facets), to set the range selector range\n const queryBundleRequest = useMemo(() => {\n const requestCopy = getCurrentQueryRequest()\n requestCopy.partMask = SynapseConstants.BUNDLE_MASK_QUERY_FACETS\n const { selectedFacets } = requestCopy.query\n requestCopy.query.selectedFacets = selectedFacets\n ? selectedFacets.filter(\n facetRequest =>\n facetRequest.columnName !== col1Name &&\n facetRequest.columnName !== col2Name,\n )\n : []\n return requestCopy\n }, [col1Name, col2Name, getCurrentQueryRequest])\n\n const { data: queryResultFacetResponse, isLoading: isLoadingFacetStats } =\n useGetQueryResultBundleWithAsyncStatus(queryBundleRequest)\n\n const fullFacetStats = queryResultFacetResponse?.responseBody?.facets\n\n const col1Facet = fullFacetStats?.find(facet => facet.columnName === col1Name)\n const col2Facet = fullFacetStats?.find(facet => facet.columnName === col2Name)\n const col1GlobalMin = (col1Facet as FacetColumnResultRange)?.columnMin\n const col2GlobalMax = (col2Facet as FacetColumnResultRange)?.columnMax\n const selectedMin = col2SelectedMin\n const selectedMax = col1SelectedMax\n\n if (isLoadingFacetStats || !col1Facet || !col2Facet) {\n return <Skeleton variant=\"rectangular\" width=\"100\" />\n }\n return (\n <RangeFacetFilterUI\n label={getColumnDisplayName(label)}\n facetResult={{\n columnMin: col1GlobalMin,\n columnMax: col2GlobalMax,\n selectedMin: selectedMin,\n selectedMax: selectedMax,\n }}\n columnType={columnType}\n onRangeValueSelected={(values: RangeValues) => {\n setRangeFacetValue(\n col1Facet,\n col1GlobalMin,\n isNumber(values.max) ? String(values.max) : values.max,\n { noCommit: true },\n )\n\n setRangeFacetValue(\n col2Facet,\n isNumber(values.min) ? String(values.min) : values.min,\n col2GlobalMax,\n )\n }}\n onNotSetSelected={() => {\n setRangeFacetValue(col1Facet, VALUE_NOT_SET, VALUE_NOT_SET)\n setRangeFacetValue(col2Facet, VALUE_NOT_SET, VALUE_NOT_SET)\n }}\n onAnySelected={() => {\n removeSelectedFacet(col1Facet)\n removeSelectedFacet(col2Facet)\n }}\n />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAuCA,SAAgB,EAAyB,EACvC,iBACA,UACA,iBACgC;CAChC,IAAM,EAAE,2BAAwB,uBAAoB,2BAClD,GAAiB,EACb,EAAE,4BAAyB,GAA8B,EAEzD,EACJ,YAAY,GAGZ,aAAa,MACX,EAAa,IACX,EACJ,YAAY,GAEZ,aAAa,MAEX,EAAa,IAiBX,EAAE,MAAM,GAA0B,WAAW,MACjD,EAfyB,QAAc;EACvC,IAAM,IAAc,GAAwB;AAC5C,IAAY,WAAW;EACvB,IAAM,EAAE,sBAAmB,EAAY;AAQvC,SAPA,EAAY,MAAM,iBAAiB,IAC/B,EAAe,QACb,MACE,EAAa,eAAe,KAC5B,EAAa,eAAe,EAC/B,GACD,EAAE,EACC;IACN;EAAC;EAAU;EAAU;EAAuB,
|
|
1
|
+
{"version":3,"file":"CombinedRangeFacetFilter.js","names":[],"sources":["../../../../src/components/widgets/query-filter/CombinedRangeFacetFilter.tsx"],"sourcesContent":["import { useGetQueryResultBundleWithAsyncStatus } from '@/synapse-queries'\nimport { SynapseConstants } from '@/utils'\nimport { VALUE_NOT_SET } from '@/utils/SynapseConstants'\nimport { Skeleton } from '@mui/material'\nimport {\n ColumnType,\n FacetColumnResultRange,\n} from '@sage-bionetworks/synapse-types'\nimport { isNumber } from 'lodash-es'\nimport { useMemo } from 'react'\nimport { useQueryContext } from '../../QueryContext'\nimport { useQueryVisualizationContext } from '../../QueryVisualizationWrapper'\nimport { RangeValues } from '../Range'\nimport { RangeFacetFilterUI } from './RangeFacetFilterUI'\n\nexport type CombinedRangeFacetFilterProps = {\n facetResults: FacetColumnResultRange[]\n label: string\n columnType: ColumnType\n}\n\n/**\n * Inclusive range selector across two columns\n * Written for the ELITE portal cohort builder, may have other uses.\n * The following diagram shows how the Range Selector min and max values are used\n * to define the facet range values:\n * \n\n Range Selector \n min ├────────────────────────┤ max\n \n Min Column facet \n Column Min value ◄────────────────────────────┤ Range Selector max\n \n Max Column facet\n Range Selector min ├────────────────────────────► Column Max value\n \n\n*/\nexport function CombinedRangeFacetFilter({\n facetResults,\n label,\n columnType,\n}: CombinedRangeFacetFilterProps) {\n const { getCurrentQueryRequest, setRangeFacetValue, removeSelectedFacet } =\n useQueryContext()\n const { getColumnDisplayName } = useQueryVisualizationContext()\n\n const {\n columnName: col1Name,\n // columnMax: col1Max // not used\n // selectedMin: col1SelectedMin // not used\n selectedMax: col1SelectedMax,\n } = facetResults[0]\n const {\n columnName: col2Name,\n //columnMin: col2Min, // not used\n selectedMin: col2SelectedMin,\n // selectedMax: col2SelectedMax, // not used\n } = facetResults[1]\n\n // Also query for the facet columns full range (without any selected facets), to set the range selector range\n const queryBundleRequest = useMemo(() => {\n const requestCopy = getCurrentQueryRequest()\n requestCopy.partMask = SynapseConstants.BUNDLE_MASK_QUERY_FACETS\n const { selectedFacets } = requestCopy.query\n requestCopy.query.selectedFacets = selectedFacets\n ? selectedFacets.filter(\n facetRequest =>\n facetRequest.columnName !== col1Name &&\n facetRequest.columnName !== col2Name,\n )\n : []\n return requestCopy\n }, [col1Name, col2Name, getCurrentQueryRequest])\n\n const { data: queryResultFacetResponse, isLoading: isLoadingFacetStats } =\n useGetQueryResultBundleWithAsyncStatus(queryBundleRequest)\n\n const fullFacetStats = queryResultFacetResponse?.responseBody?.facets\n\n const col1Facet = fullFacetStats?.find(facet => facet.columnName === col1Name)\n const col2Facet = fullFacetStats?.find(facet => facet.columnName === col2Name)\n const col1GlobalMin = (col1Facet as FacetColumnResultRange)?.columnMin\n const col2GlobalMax = (col2Facet as FacetColumnResultRange)?.columnMax\n const selectedMin = col2SelectedMin\n const selectedMax = col1SelectedMax\n\n if (isLoadingFacetStats || !col1Facet || !col2Facet) {\n return <Skeleton variant=\"rectangular\" width=\"100\" />\n }\n return (\n <RangeFacetFilterUI\n label={getColumnDisplayName(label)}\n facetResult={{\n columnMin: col1GlobalMin,\n columnMax: col2GlobalMax,\n selectedMin: selectedMin,\n selectedMax: selectedMax,\n }}\n columnType={columnType}\n onRangeValueSelected={(values: RangeValues) => {\n setRangeFacetValue(\n col1Facet,\n col1GlobalMin,\n isNumber(values.max) ? String(values.max) : values.max,\n { noCommit: true },\n )\n\n setRangeFacetValue(\n col2Facet,\n isNumber(values.min) ? String(values.min) : values.min,\n col2GlobalMax,\n )\n }}\n onNotSetSelected={() => {\n setRangeFacetValue(col1Facet, VALUE_NOT_SET, VALUE_NOT_SET)\n setRangeFacetValue(col2Facet, VALUE_NOT_SET, VALUE_NOT_SET)\n }}\n onAnySelected={() => {\n removeSelectedFacet(col1Facet)\n removeSelectedFacet(col2Facet)\n }}\n />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAuCA,SAAgB,EAAyB,EACvC,iBACA,UACA,iBACgC;CAChC,IAAM,EAAE,2BAAwB,uBAAoB,2BAClD,GAAiB,EACb,EAAE,4BAAyB,GAA8B,EAEzD,EACJ,YAAY,GAGZ,aAAa,MACX,EAAa,IACX,EACJ,YAAY,GAEZ,aAAa,MAEX,EAAa,IAiBX,EAAE,MAAM,GAA0B,WAAW,MACjD,EAfyB,QAAc;EACvC,IAAM,IAAc,GAAwB;AAC5C,IAAY,WAAW;EACvB,IAAM,EAAE,sBAAmB,EAAY;AAQvC,SAPA,EAAY,MAAM,iBAAiB,IAC/B,EAAe,QACb,MACE,EAAa,eAAe,KAC5B,EAAa,eAAe,EAC/B,GACD,EAAE,EACC;IACN;EAAC;EAAU;EAAU;EAAuB,CAGN,CAAmB,EAEtD,IAAiB,GAA0B,cAAc,QAEzD,IAAY,GAAgB,MAAK,MAAS,EAAM,eAAe,EAAS,EACxE,IAAY,GAAgB,MAAK,MAAS,EAAM,eAAe,EAAS,EACxE,IAAiB,GAAsC,WACvD,IAAiB,GAAsC,WACvD,IAAc,GACd,IAAc;AAKpB,QAHI,KAAuB,CAAC,KAAa,CAAC,IACjC,kBAAC,GAAD;EAAU,SAAQ;EAAc,OAAM;EAAQ,CAAA,GAGrD,kBAAC,GAAD;EACE,OAAO,EAAqB,EAAM;EAClC,aAAa;GACX,WAAW;GACX,WAAW;GACE;GACA;GACd;EACW;EACZ,uBAAuB,MAAwB;AAQ7C,GAPA,EACE,GACA,GACA,EAAS,EAAO,IAAI,GAAG,OAAO,EAAO,IAAI,GAAG,EAAO,KACnD,EAAE,UAAU,IAAM,CACnB,EAED,EACE,GACA,EAAS,EAAO,IAAI,GAAG,OAAO,EAAO,IAAI,GAAG,EAAO,KACnD,EACD;;EAEH,wBAAwB;AAEtB,GADA,EAAmB,GAAW,GAAe,EAAc,EAC3D,EAAmB,GAAW,GAAe,EAAc;;EAE7D,qBAAqB;AAEnB,GADA,EAAoB,EAAU,EAC9B,EAAoB,EAAU;;EAEhC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnumFacetFilter.js","names":[],"sources":["../../../../../src/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.tsx"],"sourcesContent":["import { isFacetColumnValuesRequest, SynapseConstants } from '@/utils'\nimport {\n getCorrespondingColumnForFacet,\n getCorrespondingSelectedFacet,\n} from '@/utils/functions/queryUtils'\nimport useGetInfoFromIds from '@/utils/hooks/useGetInfoFromIds'\nimport {\n ColumnTypeEnum,\n EntityHeader,\n Evaluation,\n FacetColumnRequest,\n FacetColumnResultValueCount,\n FacetColumnResultValues,\n FacetColumnValuesRequest,\n QueryBundleRequest,\n UserGroupHeader,\n} from '@sage-bionetworks/synapse-types'\nimport { cloneDeep, partition, pick, sortBy } from 'lodash-es'\nimport { Suspense, useMemo } from 'react'\nimport { useQueryContext } from '../../../QueryContext'\nimport { useQueryVisualizationContext } from '../../../QueryVisualizationWrapper'\nimport { useSuspenseGetQueryMetadata } from '../../../QueryWrapper/useGetQueryMetadata'\nimport { EnumFacetFilterSkeleton } from './EnumFacetFilterSkeleton'\nimport EnumFacetFilterUI, { RenderedFacetValue } from './EnumFacetFilterUI'\nimport { getAllIsSelected, valueToLabel } from './EnumFacetFilterUtils'\n\nexport type EnumFacetFilterProps = {\n facet: FacetColumnResultValues\n containerAs?: 'Collapsible' | 'Dropdown'\n dropdownType?: 'Icon' | 'SelectBox'\n hideCollapsible?: boolean\n defaultShowAllValues?: boolean\n}\n\nfunction EnumFacetFilterInternal(props: EnumFacetFilterProps) {\n const {\n facet,\n containerAs = 'Collapsible',\n dropdownType = 'Icon',\n hideCollapsible = false,\n defaultShowAllValues = false,\n } = props\n const {\n nextQueryRequest,\n addValueToSelectedFacet,\n removeSelectedFacet,\n removeValueFromSelectedFacet,\n resetDebounceTimer,\n } = useQueryContext()\n\n const { data: queryMetadata } = useSuspenseGetQueryMetadata()\n const { getColumnDisplayName } = useQueryVisualizationContext()\n\n const currentSelectedFacet: FacetColumnValuesRequest | undefined =\n useMemo(() => {\n const facetColumnRequest = getCorrespondingSelectedFacet(\n facet,\n cloneDeep(nextQueryRequest.query.selectedFacets) as\n | FacetColumnRequest[]\n | undefined,\n )\n\n if (\n facetColumnRequest &&\n !isFacetColumnValuesRequest(facetColumnRequest)\n ) {\n console.error(\n `The facet rendered in EnumFacetFilter is not a FacetColumnValuesRequest`,\n facet,\n )\n return undefined\n }\n return facetColumnRequest\n }, [facet, nextQueryRequest.query.selectedFacets])\n\n // Must compare the known facet values to the \"uncommitted\" current query\n const allIsSelected = getAllIsSelected(\n cloneDeep(nextQueryRequest) as QueryBundleRequest,\n facet,\n )\n\n const columnModel = queryMetadata.columnModels\n ? getCorrespondingColumnForFacet(facet, queryMetadata.columnModels)\n : undefined\n\n const isNumberColumnType = useMemo(() => {\n switch (columnModel?.columnType) {\n case ColumnTypeEnum.DOUBLE:\n case ColumnTypeEnum.DATE:\n case ColumnTypeEnum.INTEGER:\n return true\n default:\n return false\n }\n }, [columnModel])\n\n const userIds =\n columnModel?.columnType === ColumnTypeEnum.USERID ||\n columnModel?.columnType === ColumnTypeEnum.USERID_LIST\n ? facet.facetValues.map(facet => facet.value)\n : []\n const userGroupHeaders = useGetInfoFromIds<UserGroupHeader>({\n ids: userIds,\n type: 'USER_PROFILE',\n })\n\n const entityIds =\n columnModel?.columnType === ColumnTypeEnum.ENTITYID ||\n columnModel?.columnType === ColumnTypeEnum.ENTITYID_LIST\n ? facet.facetValues.map(facet => facet.value)\n : []\n const entityHeaders = useGetInfoFromIds<EntityHeader>({\n ids: entityIds,\n type: 'ENTITY_HEADER',\n })\n\n const evaluationIds =\n columnModel?.columnType === ColumnTypeEnum.EVALUATIONID\n ? facet.facetValues.map(facet => facet.value)\n : []\n const evaluations = useGetInfoFromIds<Evaluation>({\n ids: evaluationIds,\n type: 'EVALUATION_QUEUE',\n })\n\n const displayedFacetValues: RenderedFacetValue[] = useMemo(() => {\n const renderedFacetValues = facet.facetValues.map(\n (facetValue: FacetColumnResultValueCount): RenderedFacetValue => {\n return {\n ...facetValue,\n // Selected status should be based on the 'nextQuery', not the result data\n // This ensures the checkboxes respond instantly to user interaction, like while waiting for multiple changes to debounce\n isSelected:\n currentSelectedFacet?.facetValues.includes(facetValue.value) ??\n false,\n displayText: valueToLabel(\n facetValue,\n userGroupHeaders,\n entityHeaders,\n evaluations,\n ),\n }\n },\n )\n //Abby V's suggestion, always show the VALUE_NOT_SET facet value on the bottom of this sorted list\n const partitions = partition(\n renderedFacetValues,\n facet => facet.value === SynapseConstants.VALUE_NOT_SET,\n )\n const valueNotSetFacetArray = partitions[0]\n const restOfFacetValuesArray = partitions[1]\n\n // Apply client-side sorting if no server-side sort is specified\n let sortedValues: RenderedFacetValue[] = restOfFacetValuesArray\n const isClientSideSort =\n columnModel == undefined || columnModel.facetSortConfig == undefined\n if (isClientSideSort) {\n if (isNumberColumnType) {\n sortedValues = sortBy(restOfFacetValuesArray, fv => Number(fv.value))\n } else {\n sortedValues = sortBy(restOfFacetValuesArray, fv =>\n fv.displayText.toLowerCase(),\n )\n }\n }\n\n return [...sortedValues, ...valueNotSetFacetArray]\n }, [\n facet.facetValues,\n columnModel,\n currentSelectedFacet?.facetValues,\n userGroupHeaders,\n entityHeaders,\n evaluations,\n isNumberColumnType,\n ])\n\n if (!columnModel) {\n return <></>\n }\n\n return (\n <EnumFacetFilterUI\n facetTitle={getColumnDisplayName(facet.columnName, facet.jsonPath)}\n filterIsActive={!allIsSelected}\n facetValues={displayedFacetValues}\n containerAs={containerAs}\n dropdownType={dropdownType}\n hideCollapsible={hideCollapsible}\n defaultShowAllValues={defaultShowAllValues}\n onHoverOverValue={() => {\n // SWC-6698: delay the query execution (via the debounce) when an item is hovered over\n resetDebounceTimer()\n }}\n onAddValueToSelection={value => {\n addValueToSelectedFacet(\n pick(facet, ['columnName', 'jsonPath']),\n value,\n { debounce: true },\n )\n }}\n onRemoveValueFromSelection={value => {\n removeValueFromSelectedFacet(\n pick(facet, ['columnName', 'jsonPath']),\n value,\n { debounce: true },\n )\n }}\n onRemoveAllFacetSelections={() =>\n removeSelectedFacet(pick(facet, ['columnName', 'jsonPath']))\n }\n canMultiSelect={true}\n />\n )\n}\n\nexport function EnumFacetFilter(props: EnumFacetFilterProps) {\n const { containerAs = 'Collapsible', dropdownType = 'Icon' } = props\n return (\n <Suspense\n fallback={\n <EnumFacetFilterSkeleton\n containerAs={containerAs}\n dropdownType={dropdownType}\n />\n }\n >\n <EnumFacetFilterInternal {...props} />\n </Suspense>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkCA,SAAS,EAAwB,GAA6B;CAC5D,IAAM,EACJ,UACA,iBAAc,eACd,kBAAe,QACf,qBAAkB,IAClB,0BAAuB,OACrB,GACE,EACJ,qBACA,4BACA,wBACA,iCACA,0BACE,GAAiB,EAEf,EAAE,MAAM,MAAkB,GAA6B,EACvD,EAAE,4BAAyB,GAA8B,EAEzD,IACJ,QAAc;EACZ,IAAM,IAAqB,EACzB,GACA,EAAU,EAAiB,MAAM,eAAe,CAGjD;AAED,MACE,KACA,CAAC,EAA2B,EAAmB,EAC/C;AACA,WAAQ,MACN,2EACA,EACD;AACD;;AAEF,SAAO;IACN,CAAC,GAAO,EAAiB,MAAM,eAAe,CAAC,EAG9C,IAAgB,EACpB,EAAU,EAAiB,EAC3B,EACD,EAEK,IAAc,EAAc,eAC9B,EAA+B,GAAO,EAAc,aAAa,GACjE,KAAA,GAEE,IAAqB,QAAc;AACvC,UAAQ,GAAa,YAArB;GACE,KAAK,EAAe;GACpB,KAAK,EAAe;GACpB,KAAK,EAAe,QAClB,QAAO;GACT,QACE,QAAO;;IAEV,CAAC,EAAY,CAAC,EAOX,IAAmB,EAAmC;EAC1D,KALA,GAAa,eAAe,EAAe,UAC3C,GAAa,eAAe,EAAe,cACvC,EAAM,YAAY,KAAI,MAAS,EAAM,MAAM,GAC3C,EAAE;EAGN,MAAM;EACP,CAAC,EAOI,IAAgB,EAAgC;EACpD,KALA,GAAa,eAAe,EAAe,YAC3C,GAAa,eAAe,EAAe,gBACvC,EAAM,YAAY,KAAI,MAAS,EAAM,MAAM,GAC3C,EAAE;EAGN,MAAM;EACP,CAAC,EAMI,IAAc,EAA8B;EAChD,KAJA,GAAa,eAAe,EAAe,eACvC,EAAM,YAAY,KAAI,MAAS,EAAM,MAAM,GAC3C,EAAE;EAGN,MAAM;EACP,CAAC,EAEI,IAA6C,QAAc;EAoB/D,IAAM,IAAa,EAnBS,EAAM,YAAY,KAC3C,OACQ;GACL,GAAG;GAGH,YACE,GAAsB,YAAY,SAAS,EAAW,MAAM,IAC5D;GACF,aAAa,EACX,GACA,GACA,GACA,EACD;GACF,
|
|
1
|
+
{"version":3,"file":"EnumFacetFilter.js","names":[],"sources":["../../../../../src/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.tsx"],"sourcesContent":["import { isFacetColumnValuesRequest, SynapseConstants } from '@/utils'\nimport {\n getCorrespondingColumnForFacet,\n getCorrespondingSelectedFacet,\n} from '@/utils/functions/queryUtils'\nimport useGetInfoFromIds from '@/utils/hooks/useGetInfoFromIds'\nimport {\n ColumnTypeEnum,\n EntityHeader,\n Evaluation,\n FacetColumnRequest,\n FacetColumnResultValueCount,\n FacetColumnResultValues,\n FacetColumnValuesRequest,\n QueryBundleRequest,\n UserGroupHeader,\n} from '@sage-bionetworks/synapse-types'\nimport { cloneDeep, partition, pick, sortBy } from 'lodash-es'\nimport { Suspense, useMemo } from 'react'\nimport { useQueryContext } from '../../../QueryContext'\nimport { useQueryVisualizationContext } from '../../../QueryVisualizationWrapper'\nimport { useSuspenseGetQueryMetadata } from '../../../QueryWrapper/useGetQueryMetadata'\nimport { EnumFacetFilterSkeleton } from './EnumFacetFilterSkeleton'\nimport EnumFacetFilterUI, { RenderedFacetValue } from './EnumFacetFilterUI'\nimport { getAllIsSelected, valueToLabel } from './EnumFacetFilterUtils'\n\nexport type EnumFacetFilterProps = {\n facet: FacetColumnResultValues\n containerAs?: 'Collapsible' | 'Dropdown'\n dropdownType?: 'Icon' | 'SelectBox'\n hideCollapsible?: boolean\n defaultShowAllValues?: boolean\n}\n\nfunction EnumFacetFilterInternal(props: EnumFacetFilterProps) {\n const {\n facet,\n containerAs = 'Collapsible',\n dropdownType = 'Icon',\n hideCollapsible = false,\n defaultShowAllValues = false,\n } = props\n const {\n nextQueryRequest,\n addValueToSelectedFacet,\n removeSelectedFacet,\n removeValueFromSelectedFacet,\n resetDebounceTimer,\n } = useQueryContext()\n\n const { data: queryMetadata } = useSuspenseGetQueryMetadata()\n const { getColumnDisplayName } = useQueryVisualizationContext()\n\n const currentSelectedFacet: FacetColumnValuesRequest | undefined =\n useMemo(() => {\n const facetColumnRequest = getCorrespondingSelectedFacet(\n facet,\n cloneDeep(nextQueryRequest.query.selectedFacets) as\n | FacetColumnRequest[]\n | undefined,\n )\n\n if (\n facetColumnRequest &&\n !isFacetColumnValuesRequest(facetColumnRequest)\n ) {\n console.error(\n `The facet rendered in EnumFacetFilter is not a FacetColumnValuesRequest`,\n facet,\n )\n return undefined\n }\n return facetColumnRequest\n }, [facet, nextQueryRequest.query.selectedFacets])\n\n // Must compare the known facet values to the \"uncommitted\" current query\n const allIsSelected = getAllIsSelected(\n cloneDeep(nextQueryRequest) as QueryBundleRequest,\n facet,\n )\n\n const columnModel = queryMetadata.columnModels\n ? getCorrespondingColumnForFacet(facet, queryMetadata.columnModels)\n : undefined\n\n const isNumberColumnType = useMemo(() => {\n switch (columnModel?.columnType) {\n case ColumnTypeEnum.DOUBLE:\n case ColumnTypeEnum.DATE:\n case ColumnTypeEnum.INTEGER:\n return true\n default:\n return false\n }\n }, [columnModel])\n\n const userIds =\n columnModel?.columnType === ColumnTypeEnum.USERID ||\n columnModel?.columnType === ColumnTypeEnum.USERID_LIST\n ? facet.facetValues.map(facet => facet.value)\n : []\n const userGroupHeaders = useGetInfoFromIds<UserGroupHeader>({\n ids: userIds,\n type: 'USER_PROFILE',\n })\n\n const entityIds =\n columnModel?.columnType === ColumnTypeEnum.ENTITYID ||\n columnModel?.columnType === ColumnTypeEnum.ENTITYID_LIST\n ? facet.facetValues.map(facet => facet.value)\n : []\n const entityHeaders = useGetInfoFromIds<EntityHeader>({\n ids: entityIds,\n type: 'ENTITY_HEADER',\n })\n\n const evaluationIds =\n columnModel?.columnType === ColumnTypeEnum.EVALUATIONID\n ? facet.facetValues.map(facet => facet.value)\n : []\n const evaluations = useGetInfoFromIds<Evaluation>({\n ids: evaluationIds,\n type: 'EVALUATION_QUEUE',\n })\n\n const displayedFacetValues: RenderedFacetValue[] = useMemo(() => {\n const renderedFacetValues = facet.facetValues.map(\n (facetValue: FacetColumnResultValueCount): RenderedFacetValue => {\n return {\n ...facetValue,\n // Selected status should be based on the 'nextQuery', not the result data\n // This ensures the checkboxes respond instantly to user interaction, like while waiting for multiple changes to debounce\n isSelected:\n currentSelectedFacet?.facetValues.includes(facetValue.value) ??\n false,\n displayText: valueToLabel(\n facetValue,\n userGroupHeaders,\n entityHeaders,\n evaluations,\n ),\n }\n },\n )\n //Abby V's suggestion, always show the VALUE_NOT_SET facet value on the bottom of this sorted list\n const partitions = partition(\n renderedFacetValues,\n facet => facet.value === SynapseConstants.VALUE_NOT_SET,\n )\n const valueNotSetFacetArray = partitions[0]\n const restOfFacetValuesArray = partitions[1]\n\n // Apply client-side sorting if no server-side sort is specified\n let sortedValues: RenderedFacetValue[] = restOfFacetValuesArray\n const isClientSideSort =\n columnModel == undefined || columnModel.facetSortConfig == undefined\n if (isClientSideSort) {\n if (isNumberColumnType) {\n sortedValues = sortBy(restOfFacetValuesArray, fv => Number(fv.value))\n } else {\n sortedValues = sortBy(restOfFacetValuesArray, fv =>\n fv.displayText.toLowerCase(),\n )\n }\n }\n\n return [...sortedValues, ...valueNotSetFacetArray]\n }, [\n facet.facetValues,\n columnModel,\n currentSelectedFacet?.facetValues,\n userGroupHeaders,\n entityHeaders,\n evaluations,\n isNumberColumnType,\n ])\n\n if (!columnModel) {\n return <></>\n }\n\n return (\n <EnumFacetFilterUI\n facetTitle={getColumnDisplayName(facet.columnName, facet.jsonPath)}\n filterIsActive={!allIsSelected}\n facetValues={displayedFacetValues}\n containerAs={containerAs}\n dropdownType={dropdownType}\n hideCollapsible={hideCollapsible}\n defaultShowAllValues={defaultShowAllValues}\n onHoverOverValue={() => {\n // SWC-6698: delay the query execution (via the debounce) when an item is hovered over\n resetDebounceTimer()\n }}\n onAddValueToSelection={value => {\n addValueToSelectedFacet(\n pick(facet, ['columnName', 'jsonPath']),\n value,\n { debounce: true },\n )\n }}\n onRemoveValueFromSelection={value => {\n removeValueFromSelectedFacet(\n pick(facet, ['columnName', 'jsonPath']),\n value,\n { debounce: true },\n )\n }}\n onRemoveAllFacetSelections={() =>\n removeSelectedFacet(pick(facet, ['columnName', 'jsonPath']))\n }\n canMultiSelect={true}\n />\n )\n}\n\nexport function EnumFacetFilter(props: EnumFacetFilterProps) {\n const { containerAs = 'Collapsible', dropdownType = 'Icon' } = props\n return (\n <Suspense\n fallback={\n <EnumFacetFilterSkeleton\n containerAs={containerAs}\n dropdownType={dropdownType}\n />\n }\n >\n <EnumFacetFilterInternal {...props} />\n </Suspense>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkCA,SAAS,EAAwB,GAA6B;CAC5D,IAAM,EACJ,UACA,iBAAc,eACd,kBAAe,QACf,qBAAkB,IAClB,0BAAuB,OACrB,GACE,EACJ,qBACA,4BACA,wBACA,iCACA,0BACE,GAAiB,EAEf,EAAE,MAAM,MAAkB,GAA6B,EACvD,EAAE,4BAAyB,GAA8B,EAEzD,IACJ,QAAc;EACZ,IAAM,IAAqB,EACzB,GACA,EAAU,EAAiB,MAAM,eAAe,CAGjD;AAED,MACE,KACA,CAAC,EAA2B,EAAmB,EAC/C;AACA,WAAQ,MACN,2EACA,EACD;AACD;;AAEF,SAAO;IACN,CAAC,GAAO,EAAiB,MAAM,eAAe,CAAC,EAG9C,IAAgB,EACpB,EAAU,EAAiB,EAC3B,EACD,EAEK,IAAc,EAAc,eAC9B,EAA+B,GAAO,EAAc,aAAa,GACjE,KAAA,GAEE,IAAqB,QAAc;AACvC,UAAQ,GAAa,YAArB;GACE,KAAK,EAAe;GACpB,KAAK,EAAe;GACpB,KAAK,EAAe,QAClB,QAAO;GACT,QACE,QAAO;;IAEV,CAAC,EAAY,CAAC,EAOX,IAAmB,EAAmC;EAC1D,KALA,GAAa,eAAe,EAAe,UAC3C,GAAa,eAAe,EAAe,cACvC,EAAM,YAAY,KAAI,MAAS,EAAM,MAAM,GAC3C,EAAE;EAGN,MAAM;EACP,CAAC,EAOI,IAAgB,EAAgC;EACpD,KALA,GAAa,eAAe,EAAe,YAC3C,GAAa,eAAe,EAAe,gBACvC,EAAM,YAAY,KAAI,MAAS,EAAM,MAAM,GAC3C,EAAE;EAGN,MAAM;EACP,CAAC,EAMI,IAAc,EAA8B;EAChD,KAJA,GAAa,eAAe,EAAe,eACvC,EAAM,YAAY,KAAI,MAAS,EAAM,MAAM,GAC3C,EAAE;EAGN,MAAM;EACP,CAAC,EAEI,IAA6C,QAAc;EAoB/D,IAAM,IAAa,EAnBS,EAAM,YAAY,KAC3C,OACQ;GACL,GAAG;GAGH,YACE,GAAsB,YAAY,SAAS,EAAW,MAAM,IAC5D;GACF,aAAa,EACX,GACA,GACA,GACA,EACD;GACF,EAKH,GACA,MAAS,EAAM,UAAU,EAC1B,EACK,IAAwB,EAAW,IACnC,IAAyB,EAAW,IAGtC,IAAqC;AAazC,UAXE,KAAe,QAAa,EAAY,mBAAmB,UAE3D,AAGE,IAHE,IACa,EAAO,IAAwB,MAAM,OAAO,EAAG,MAAM,CAAC,GAEtD,EAAO,IAAwB,MAC5C,EAAG,YAAY,aAAa,CAC7B,GAIE,CAAC,GAAG,GAAc,GAAG,EAAsB;IACjD;EACD,EAAM;EACN;EACA,GAAsB;EACtB;EACA;EACA;EACA;EACD,CAAC;AAMF,QAJK,IAKH,kBAAC,GAAD;EACE,YAAY,EAAqB,EAAM,YAAY,EAAM,SAAS;EAClE,gBAAgB,CAAC;EACjB,aAAa;EACA;EACC;EACG;EACK;EACtB,wBAAwB;AAEtB,MAAoB;;EAEtB,wBAAuB,MAAS;AAC9B,KACE,EAAK,GAAO,CAAC,cAAc,WAAW,CAAC,EACvC,GACA,EAAE,UAAU,IAAM,CACnB;;EAEH,6BAA4B,MAAS;AACnC,KACE,EAAK,GAAO,CAAC,cAAc,WAAW,CAAC,EACvC,GACA,EAAE,UAAU,IAAM,CACnB;;EAEH,kCACE,EAAoB,EAAK,GAAO,CAAC,cAAc,WAAW,CAAC,CAAC;EAE9D,gBAAgB;EAChB,CAAA,GAlCK,kBAAA,GAAA,EAAK,CAAA;;AAsChB,SAAgB,EAAgB,GAA6B;CAC3D,IAAM,EAAE,iBAAc,eAAe,kBAAe,WAAW;AAC/D,QACE,kBAAC,GAAD;EACE,UACE,kBAAC,GAAD;GACe;GACC;GACd,CAAA;YAGJ,kBAAC,GAAD,EAAyB,GAAI,GAAS,CAAA;EAC7B,CAAA"}
|