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":"ColumnModelValidator.js","names":[],"sources":["../../../../src/components/TableColumnSchemaEditor/Validators/ColumnModelValidator.ts"],"sourcesContent":["import { ColumnModel, ColumnTypeEnum } from '@sage-bionetworks/synapse-types'\nimport {\n canHaveMaxListLength,\n canHaveSize,\n MAX_LIST_LENGTH,\n MAX_STRING_SIZE,\n} from '../TableColumnSchemaEditorUtils'\nimport { type SafeParseReturnType, z } from 'zod'\nimport { SetOptional } from 'type-fest'\nimport { getDefaultValueValidator } from './DefaultValueValidator'\nimport getEnumValuesValidator from './EnumValuesValidator'\nimport { optionalStringSchema } from './OptionalStringSchema'\nimport { omit } from 'lodash-es'\n\nconst facetTypeSchema = z.union([z.literal('enumeration'), z.literal('range')])\n\nconst facetColumnSortConfigSchema = z.object({\n property: z.enum(['FREQUENCY', 'VALUE']).optional(),\n direction: z.enum(['DESC', 'ASC']).optional(),\n})\n\nconst columnModelBaseZodSchema = z.object({\n name: z.string().min(1, { message: 'Name is required' }),\n columnType: z.nativeEnum(ColumnTypeEnum),\n})\n\nexport const jsonSubColumnModelZodSchema = columnModelBaseZodSchema.merge(\n z.object({\n jsonPath: z.string().startsWith('$'),\n facetType: facetTypeSchema,\n }),\n)\n\n/**\n * Zod schema whose shape represents a ColumnModel. This unrefined schema is saved in this \"intermediate\" state so\n * that it can be extended with `.merge` to create an inferred type that can applied to form data.\n */\nconst unrefinedColumnModelSchema = columnModelBaseZodSchema.merge(\n z.object({\n id: z.string().optional(),\n defaultValue: z.union([z.string(), z.array(z.any())]).optional(),\n maximumSize: z\n .union([optionalStringSchema, z.number()])\n .pipe(\n z.coerce.number().finite().int().min(1).max(MAX_STRING_SIZE).optional(),\n ),\n maximumListLength: z\n .union([optionalStringSchema, z.number()])\n .pipe(\n z.coerce.number().finite().int().min(1).max(MAX_LIST_LENGTH).optional(),\n ),\n enumValues: z\n .array(\n z\n .union([\n z.string(),\n // enumValues is allowed on INTEGER type, but the backend stores them as strings, so allow passing integers\n z.number().int(),\n ])\n .pipe(z.coerce.string()),\n )\n .optional(),\n jsonSubColumns: z.array(jsonSubColumnModelZodSchema).optional(),\n facetType: facetTypeSchema.optional(),\n facetSortConfig: facetColumnSortConfigSchema.optional(),\n }),\n)\n\n/**\n * Zod schema to validate a column model. The provided data is coerced and transformed to match the ColumnModel type.\n * For this reason, form data may be directly passed into this schema parser.\n *\n * The parse method will return a ColumnModel where all data is properly formed.\n */\nexport const columnModelZodSchema = unrefinedColumnModelSchema\n .refine(data => data.maximumSize == null || canHaveSize(data.columnType), {\n message: 'Size is not allowed for this column type',\n path: ['maximumSize'],\n })\n .refine(\n data =>\n data.maximumListLength == null || canHaveMaxListLength(data.columnType),\n {\n message: 'Maximum list length is not allowed for this column type',\n path: ['maximumListLength'],\n },\n )\n .refine(\n data => {\n if (data.jsonSubColumns != null) {\n return data.columnType === ColumnTypeEnum.JSON\n }\n return true\n },\n {\n message: 'Only JSON columns can have JSON sub-columns.',\n path: ['columnType'],\n },\n )\n .transform((data, ctx) => {\n if (data.defaultValue != null) {\n // Validate and transform the defaultValue based on the columnType\n const defaultValueSchema = getDefaultValueValidator(data.columnType)\n const result = defaultValueSchema.safeParse(data.defaultValue)\n let transformedValue: string | undefined\n if (result.success) {\n transformedValue = result.data\n } else {\n result.error.issues.forEach(issue => {\n ctx.addIssue({ ...issue, path: ['defaultValue'] })\n })\n transformedValue = undefined\n }\n return { ...data, defaultValue: transformedValue }\n }\n return omit(data, 'defaultValue')\n })\n .transform((data, ctx) => {\n // NOTE: This is the same set of steps as the `defaultValue` transform\n // We may be able to refactor this, but it is challenging to do so without breaking the schema's inferred type\n if (data.enumValues != null) {\n // Validate and transform the defaultValue based on the columnType\n const enumValuesSchema = getEnumValuesValidator(data.columnType)\n const result = enumValuesSchema.safeParse(data.enumValues)\n let transformedValue: string[] | undefined\n if (result.success) {\n transformedValue = result.data\n } else {\n result.error.issues.forEach(issue => {\n ctx.addIssue({ ...issue, path: ['enumValues'] })\n })\n transformedValue = undefined\n }\n return { ...data, enumValues: transformedValue }\n }\n return omit(data, 'enumValues')\n })\n\nexport const columnModelFormDataZodSchema = z.array(columnModelZodSchema)\n\nconst jsonSubColumnModelFormDataSchema = jsonSubColumnModelZodSchema.merge(\n z.object({\n isSelected: z.boolean(),\n }),\n)\n\nexport type JsonSubColumnModelFormData = z.input<\n typeof jsonSubColumnModelFormDataSchema\n>\n\n// Use the \"unrefined\" schema since we can extend it with form-only fields\nconst _columnModelFormDataSchema = unrefinedColumnModelSchema.merge(\n z.object({\n isSelected: z.boolean(),\n isOriginallyDefaultColumn: z.boolean(),\n jsonSubColumns: z.array(jsonSubColumnModelFormDataSchema).optional(),\n }),\n)\n\n/**\n * Type that represents possible form input data for a ColumnModel.\n * a\n */\nexport type ColumnModelFormData = z.input<typeof _columnModelFormDataSchema>\n\nexport function validateColumnModelFormData(\n formData: SetOptional<\n ColumnModelFormData,\n 'isOriginallyDefaultColumn' | 'isSelected'\n >[],\n): SafeParseReturnType<\n SetOptional<\n ColumnModelFormData,\n 'isOriginallyDefaultColumn' | 'isSelected'\n >[],\n SetOptional<ColumnModel, 'id'>[]\n> {\n return columnModelFormDataZodSchema.safeParse(formData)\n}\n"],"mappings":";;;;;;;;AAcA,IAAM,IAAkB,EAAE,MAAM,CAAC,EAAE,QAAQ,cAAc,EAAE,EAAE,QAAQ,QAAQ,CAAC,CAAC,EAEzE,IAA8B,EAAE,OAAO;CAC3C,UAAU,EAAE,KAAK,CAAC,aAAa,QAAQ,CAAC,CAAC,UAAU;CACnD,WAAW,EAAE,KAAK,CAAC,QAAQ,MAAM,CAAC,CAAC,UAAU;CAC9C,CAAC,EAEI,IAA2B,EAAE,OAAO;CACxC,MAAM,EAAE,QAAQ,CAAC,IAAI,GAAG,EAAE,SAAS,oBAAoB,CAAC;CACxD,YAAY,EAAE,WAAW,EAAe;CACzC,CAAC,EAEW,IAA8B,EAAyB,MAClE,EAAE,OAAO;CACP,UAAU,EAAE,QAAQ,CAAC,WAAW,IAAI;CACpC,WAAW;CACZ,CAAC,CACH,EAMK,IAA6B,EAAyB,MAC1D,EAAE,OAAO;CACP,IAAI,EAAE,QAAQ,CAAC,UAAU;CACzB,cAAc,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU;CAChE,aAAa,EACV,MAAM,CAAC,GAAsB,EAAE,QAAQ,CAAC,CAAC,CACzC,KACC,EAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAgB,CAAC,UAAU,CACxE;CACH,mBAAmB,EAChB,MAAM,CAAC,GAAsB,EAAE,QAAQ,CAAC,CAAC,CACzC,KACC,EAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAA,IAAoB,CAAC,UAAU,CACxE;CACH,YAAY,EACT,MACC,EACG,MAAM,CACL,EAAE,QAAQ,EAEV,EAAE,QAAQ,CAAC,KAAK,CACjB,CAAC,CACD,KAAK,EAAE,OAAO,QAAQ,CAAC,CAC3B,CACA,UAAU;CACb,gBAAgB,EAAE,MAAM,EAA4B,CAAC,UAAU;CAC/D,WAAW,EAAgB,UAAU;CACrC,iBAAiB,EAA4B,UAAU;CACxD,CAAC,CACH,EAQY,IAAuB,EACjC,QAAO,MAAQ,EAAK,eAAe,QAAQ,EAAY,EAAK,WAAW,EAAE;CACxE,SAAS;CACT,MAAM,CAAC,cAAc;CACtB,CAAC,CACD,QACC,MACE,EAAK,qBAAqB,QAAQ,EAAqB,EAAK,WAAW,EACzE;CACE,SAAS;CACT,MAAM,CAAC,oBAAoB;CAC5B,CACF,CACA,QACC,MACM,EAAK,kBAAkB,OAGpB,KAFE,EAAK,eAAe,EAAe,MAI9C;CACE,SAAS;CACT,MAAM,CAAC,aAAa;CACrB,CACF,CACA,WAAW,GAAM,MAAQ;AACxB,KAAI,EAAK,gBAAgB,MAAM;EAG7B,IAAM,IADqB,EAAyB,EAAK,
|
|
1
|
+
{"version":3,"file":"ColumnModelValidator.js","names":[],"sources":["../../../../src/components/TableColumnSchemaEditor/Validators/ColumnModelValidator.ts"],"sourcesContent":["import { ColumnModel, ColumnTypeEnum } from '@sage-bionetworks/synapse-types'\nimport {\n canHaveMaxListLength,\n canHaveSize,\n MAX_LIST_LENGTH,\n MAX_STRING_SIZE,\n} from '../TableColumnSchemaEditorUtils'\nimport { type SafeParseReturnType, z } from 'zod'\nimport { SetOptional } from 'type-fest'\nimport { getDefaultValueValidator } from './DefaultValueValidator'\nimport getEnumValuesValidator from './EnumValuesValidator'\nimport { optionalStringSchema } from './OptionalStringSchema'\nimport { omit } from 'lodash-es'\n\nconst facetTypeSchema = z.union([z.literal('enumeration'), z.literal('range')])\n\nconst facetColumnSortConfigSchema = z.object({\n property: z.enum(['FREQUENCY', 'VALUE']).optional(),\n direction: z.enum(['DESC', 'ASC']).optional(),\n})\n\nconst columnModelBaseZodSchema = z.object({\n name: z.string().min(1, { message: 'Name is required' }),\n columnType: z.nativeEnum(ColumnTypeEnum),\n})\n\nexport const jsonSubColumnModelZodSchema = columnModelBaseZodSchema.merge(\n z.object({\n jsonPath: z.string().startsWith('$'),\n facetType: facetTypeSchema,\n }),\n)\n\n/**\n * Zod schema whose shape represents a ColumnModel. This unrefined schema is saved in this \"intermediate\" state so\n * that it can be extended with `.merge` to create an inferred type that can applied to form data.\n */\nconst unrefinedColumnModelSchema = columnModelBaseZodSchema.merge(\n z.object({\n id: z.string().optional(),\n defaultValue: z.union([z.string(), z.array(z.any())]).optional(),\n maximumSize: z\n .union([optionalStringSchema, z.number()])\n .pipe(\n z.coerce.number().finite().int().min(1).max(MAX_STRING_SIZE).optional(),\n ),\n maximumListLength: z\n .union([optionalStringSchema, z.number()])\n .pipe(\n z.coerce.number().finite().int().min(1).max(MAX_LIST_LENGTH).optional(),\n ),\n enumValues: z\n .array(\n z\n .union([\n z.string(),\n // enumValues is allowed on INTEGER type, but the backend stores them as strings, so allow passing integers\n z.number().int(),\n ])\n .pipe(z.coerce.string()),\n )\n .optional(),\n jsonSubColumns: z.array(jsonSubColumnModelZodSchema).optional(),\n facetType: facetTypeSchema.optional(),\n facetSortConfig: facetColumnSortConfigSchema.optional(),\n }),\n)\n\n/**\n * Zod schema to validate a column model. The provided data is coerced and transformed to match the ColumnModel type.\n * For this reason, form data may be directly passed into this schema parser.\n *\n * The parse method will return a ColumnModel where all data is properly formed.\n */\nexport const columnModelZodSchema = unrefinedColumnModelSchema\n .refine(data => data.maximumSize == null || canHaveSize(data.columnType), {\n message: 'Size is not allowed for this column type',\n path: ['maximumSize'],\n })\n .refine(\n data =>\n data.maximumListLength == null || canHaveMaxListLength(data.columnType),\n {\n message: 'Maximum list length is not allowed for this column type',\n path: ['maximumListLength'],\n },\n )\n .refine(\n data => {\n if (data.jsonSubColumns != null) {\n return data.columnType === ColumnTypeEnum.JSON\n }\n return true\n },\n {\n message: 'Only JSON columns can have JSON sub-columns.',\n path: ['columnType'],\n },\n )\n .transform((data, ctx) => {\n if (data.defaultValue != null) {\n // Validate and transform the defaultValue based on the columnType\n const defaultValueSchema = getDefaultValueValidator(data.columnType)\n const result = defaultValueSchema.safeParse(data.defaultValue)\n let transformedValue: string | undefined\n if (result.success) {\n transformedValue = result.data\n } else {\n result.error.issues.forEach(issue => {\n ctx.addIssue({ ...issue, path: ['defaultValue'] })\n })\n transformedValue = undefined\n }\n return { ...data, defaultValue: transformedValue }\n }\n return omit(data, 'defaultValue')\n })\n .transform((data, ctx) => {\n // NOTE: This is the same set of steps as the `defaultValue` transform\n // We may be able to refactor this, but it is challenging to do so without breaking the schema's inferred type\n if (data.enumValues != null) {\n // Validate and transform the defaultValue based on the columnType\n const enumValuesSchema = getEnumValuesValidator(data.columnType)\n const result = enumValuesSchema.safeParse(data.enumValues)\n let transformedValue: string[] | undefined\n if (result.success) {\n transformedValue = result.data\n } else {\n result.error.issues.forEach(issue => {\n ctx.addIssue({ ...issue, path: ['enumValues'] })\n })\n transformedValue = undefined\n }\n return { ...data, enumValues: transformedValue }\n }\n return omit(data, 'enumValues')\n })\n\nexport const columnModelFormDataZodSchema = z.array(columnModelZodSchema)\n\nconst jsonSubColumnModelFormDataSchema = jsonSubColumnModelZodSchema.merge(\n z.object({\n isSelected: z.boolean(),\n }),\n)\n\nexport type JsonSubColumnModelFormData = z.input<\n typeof jsonSubColumnModelFormDataSchema\n>\n\n// Use the \"unrefined\" schema since we can extend it with form-only fields\nconst _columnModelFormDataSchema = unrefinedColumnModelSchema.merge(\n z.object({\n isSelected: z.boolean(),\n isOriginallyDefaultColumn: z.boolean(),\n jsonSubColumns: z.array(jsonSubColumnModelFormDataSchema).optional(),\n }),\n)\n\n/**\n * Type that represents possible form input data for a ColumnModel.\n * a\n */\nexport type ColumnModelFormData = z.input<typeof _columnModelFormDataSchema>\n\nexport function validateColumnModelFormData(\n formData: SetOptional<\n ColumnModelFormData,\n 'isOriginallyDefaultColumn' | 'isSelected'\n >[],\n): SafeParseReturnType<\n SetOptional<\n ColumnModelFormData,\n 'isOriginallyDefaultColumn' | 'isSelected'\n >[],\n SetOptional<ColumnModel, 'id'>[]\n> {\n return columnModelFormDataZodSchema.safeParse(formData)\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatetimeSchema.js","names":[],"sources":["../../../../src/components/TableColumnSchemaEditor/Validators/DatetimeSchema.ts"],"sourcesContent":["import { z } from 'zod'\nimport dayjs from 'dayjs'\nimport utc from 'dayjs/plugin/utc'\n\ndayjs.extend(utc)\n\n// Accepts a string that is either a UNIX timestamp in milliseconds or in ISO 8601 format, and returns a string in ISO 8601 format\nconst dateTimeString = z.string().transform(data => {\n // Note: can't use parseInt on a timestamp! \"2000-01-01T00:00:00Z\" will be parsed as 2000\n const isInt = data.match(/^\\d+$/)\n if (isInt) {\n return dayjs.utc(parseInt(data)).toISOString()\n }\n return dayjs.utc(data).toISOString()\n})\n\n/**\n * Validates and converts a value to a valid Synapse DATE value\n */\nexport const dateTimeSchema = z\n .union([z.number(), dateTimeString, z.date()])\n .transform(timestampMs => dayjs(timestampMs).toISOString())\n"],"mappings":";;;;AAIA,EAAM,OAAO,EAAI;AAGjB,IAAM,IAAiB,EAAE,QAAQ,CAAC,WAAU,MAE5B,EAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"DatetimeSchema.js","names":[],"sources":["../../../../src/components/TableColumnSchemaEditor/Validators/DatetimeSchema.ts"],"sourcesContent":["import { z } from 'zod'\nimport dayjs from 'dayjs'\nimport utc from 'dayjs/plugin/utc'\n\ndayjs.extend(utc)\n\n// Accepts a string that is either a UNIX timestamp in milliseconds or in ISO 8601 format, and returns a string in ISO 8601 format\nconst dateTimeString = z.string().transform(data => {\n // Note: can't use parseInt on a timestamp! \"2000-01-01T00:00:00Z\" will be parsed as 2000\n const isInt = data.match(/^\\d+$/)\n if (isInt) {\n return dayjs.utc(parseInt(data)).toISOString()\n }\n return dayjs.utc(data).toISOString()\n})\n\n/**\n * Validates and converts a value to a valid Synapse DATE value\n */\nexport const dateTimeSchema = z\n .union([z.number(), dateTimeString, z.date()])\n .transform(timestampMs => dayjs(timestampMs).toISOString())\n"],"mappings":";;;;AAIA,EAAM,OAAO,EAAI;AAGjB,IAAM,IAAiB,EAAE,QAAQ,CAAC,WAAU,MAE5B,EAAK,MAAM,QACrB,GACK,EAAM,IAAI,SAAS,EAAK,CAAC,CAAC,aAAa,GAEzC,EAAM,IAAI,EAAK,CAAC,aAAa,CACpC,EAKW,IAAiB,EAC3B,MAAM;CAAC,EAAE,QAAQ;CAAE;CAAgB,EAAE,MAAM;CAAC,CAAC,CAC7C,WAAU,MAAe,EAAM,EAAY,CAAC,aAAa,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColumnHeader.d.ts","sourceRoot":"","sources":["../../../src/components/TanStackTable/ColumnHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAIrD,OAAO,EAAE,SAAS,EAAe,MAAM,OAAO,CAAA;AAG9C,KAAK,iBAAiB,GAAG;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,iBAAiB,CAAC,EAAE,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"ColumnHeader.d.ts","sourceRoot":"","sources":["../../../src/components/TanStackTable/ColumnHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAIrD,OAAO,EAAE,SAAS,EAAe,MAAM,OAAO,CAAA;AAG9C,KAAK,iBAAiB,GAAG;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,iBAAiB,CAAC,EAAE,SAAS,CAAA;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAA;IAGd,aAAa,CAAC,EAAE,SAAS,CAAA;CAC1B,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,GAAG,OAAO,EACpE,KAAK,EAAE,iBAAiB,GAAG,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,2CA8FxD"}
|
|
@@ -6,8 +6,8 @@ import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
|
6
6
|
import { HelpTwoTone as l } from "@mui/icons-material";
|
|
7
7
|
//#region src/components/TanStackTable/ColumnHeader.tsx
|
|
8
8
|
function u(u) {
|
|
9
|
-
let { column: d, title: f = u.column.id,
|
|
10
|
-
if (
|
|
9
|
+
let { column: d, title: f = u.column.id, wrap: p = !1, helpText: m, filterControl: h, additionalButtons: g } = u, _ = n(() => {
|
|
10
|
+
if (h) return h;
|
|
11
11
|
if (d.getCanFilter()) {
|
|
12
12
|
if (d.columnDef.meta?.filterVariant === "enumeration") return /* @__PURE__ */ s(t, {
|
|
13
13
|
column: d,
|
|
@@ -18,7 +18,7 @@ function u(u) {
|
|
|
18
18
|
return /* @__PURE__ */ s(o, {});
|
|
19
19
|
}, [
|
|
20
20
|
d,
|
|
21
|
-
|
|
21
|
+
h,
|
|
22
22
|
f
|
|
23
23
|
])();
|
|
24
24
|
return /* @__PURE__ */ c(r, {
|
|
@@ -34,7 +34,7 @@ function u(u) {
|
|
|
34
34
|
style: {
|
|
35
35
|
overflow: "hidden",
|
|
36
36
|
textOverflow: "ellipsis",
|
|
37
|
-
whiteSpace: "nowrap",
|
|
37
|
+
whiteSpace: p ? "pre-wrap" : "nowrap",
|
|
38
38
|
minWidth: 0
|
|
39
39
|
},
|
|
40
40
|
title: f,
|
|
@@ -49,15 +49,15 @@ function u(u) {
|
|
|
49
49
|
gap: .25
|
|
50
50
|
},
|
|
51
51
|
children: [
|
|
52
|
-
|
|
53
|
-
title:
|
|
52
|
+
m && /* @__PURE__ */ s(a, {
|
|
53
|
+
title: m,
|
|
54
54
|
placement: "top",
|
|
55
55
|
children: /* @__PURE__ */ s(i, {
|
|
56
56
|
size: "small",
|
|
57
57
|
children: /* @__PURE__ */ s(l, { fontSize: "inherit" })
|
|
58
58
|
})
|
|
59
59
|
}),
|
|
60
|
-
d.getCanFilter() &&
|
|
60
|
+
d.getCanFilter() && _,
|
|
61
61
|
d.getCanSort() && /* @__PURE__ */ s(a, {
|
|
62
62
|
title: `Sort by ${f}`,
|
|
63
63
|
placement: "top",
|
|
@@ -78,7 +78,7 @@ function u(u) {
|
|
|
78
78
|
})
|
|
79
79
|
})
|
|
80
80
|
}),
|
|
81
|
-
|
|
81
|
+
g
|
|
82
82
|
]
|
|
83
83
|
})]
|
|
84
84
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColumnHeader.js","names":[],"sources":["../../../src/components/TanStackTable/ColumnHeader.tsx"],"sourcesContent":["import { HeaderContext } from '@tanstack/react-table'\nimport { Box, IconButton, Tooltip } from '@mui/material'\nimport { HelpTwoTone } from '@mui/icons-material'\nimport IconSvg from '../IconSvg'\nimport { ReactNode, useCallback } from 'react'\nimport { ColumnHeaderEnumFilter } from './ColumnHeaderEnumFilter'\n\ntype ColumnHeaderProps = {\n title?: string\n helpText?: ReactNode\n additionalButtons?: ReactNode\n\n // TODO: Replace with props that can be passed to a reusable filter control\n filterControl?: ReactNode\n}\n\n/**\n * Styled table column header component for use with @tanstack/react-table, with extra optional props for additional\n * UI features\n */\nexport default function ColumnHeader<TData = unknown, TValue = unknown>(\n props: ColumnHeaderProps & HeaderContext<TData, TValue>,\n) {\n const {\n column,\n title = props.column.id,\n helpText,\n filterControl: filterControlFromProps,\n additionalButtons,\n } = props\n\n const getFilterControl = useCallback(() => {\n if (filterControlFromProps) {\n return filterControlFromProps\n }\n if (column.getCanFilter()) {\n if (column.columnDef.meta?.filterVariant === 'enumeration') {\n return <ColumnHeaderEnumFilter column={column} title={title} />\n }\n // As needed, extend ColumnMeta.filterVariant to support other types of filters\n console.warn(\n `column.getCanFilter() was true for column ID: ${column.id} but no filterControlFromProps was passed and component for filterVariant: ${column.columnDef.meta?.filterVariant} is not implemented`,\n )\n }\n return <></>\n }, [column, filterControlFromProps, title])\n\n const filterControl = getFilterControl()\n\n return (\n <Box\n sx={{\n display: 'flex',\n alignContent: 'center',\n alignItems: 'center',\n justifyContent: 'space-between',\n overflow: 'hidden',\n minWidth: 0,\n }}\n >\n <span\n style={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n minWidth: 0,\n }}\n title={title}\n >\n {title}\n </span>\n <Box\n role={'menubar'}\n sx={{\n display: 'flex',\n alignItems: 'center',\n height: '22px',\n ml: 2,\n gap: 0.25,\n }}\n >\n {helpText && (\n <Tooltip title={helpText} placement={'top'}>\n <IconButton size={'small'}>\n <HelpTwoTone fontSize={'inherit'} />\n </IconButton>\n </Tooltip>\n )}\n {column.getCanFilter() && filterControl}\n {column.getCanSort() && (\n <Tooltip title={`Sort by ${title}`} placement={'top'}>\n <IconButton\n role=\"button\"\n size={'small'}\n aria-label={`Sort by ${title}`}\n tabIndex={0}\n onKeyPress={() => column.toggleSorting()}\n onClick={() => column.toggleSorting()}\n >\n <IconSvg\n icon={column.getIsSorted() === 'asc' ? 'sortUp' : 'sortDown'}\n wrap={false}\n sx={{\n color: column.getIsSorted() ? 'primary.main' : 'grey.700',\n backgroundColor: 'none',\n }}\n />\n </IconButton>\n </Tooltip>\n )}\n {additionalButtons}\n </Box>\n </Box>\n )\n}\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"ColumnHeader.js","names":[],"sources":["../../../src/components/TanStackTable/ColumnHeader.tsx"],"sourcesContent":["import { HeaderContext } from '@tanstack/react-table'\nimport { Box, IconButton, Tooltip } from '@mui/material'\nimport { HelpTwoTone } from '@mui/icons-material'\nimport IconSvg from '../IconSvg'\nimport { ReactNode, useCallback } from 'react'\nimport { ColumnHeaderEnumFilter } from './ColumnHeaderEnumFilter'\n\ntype ColumnHeaderProps = {\n title?: string\n helpText?: ReactNode\n additionalButtons?: ReactNode\n wrap?: boolean\n\n // TODO: Replace with props that can be passed to a reusable filter control\n filterControl?: ReactNode\n}\n\n/**\n * Styled table column header component for use with @tanstack/react-table, with extra optional props for additional\n * UI features\n */\nexport default function ColumnHeader<TData = unknown, TValue = unknown>(\n props: ColumnHeaderProps & HeaderContext<TData, TValue>,\n) {\n const {\n column,\n title = props.column.id,\n wrap = false,\n helpText,\n filterControl: filterControlFromProps,\n additionalButtons,\n } = props\n\n const getFilterControl = useCallback(() => {\n if (filterControlFromProps) {\n return filterControlFromProps\n }\n if (column.getCanFilter()) {\n if (column.columnDef.meta?.filterVariant === 'enumeration') {\n return <ColumnHeaderEnumFilter column={column} title={title} />\n }\n // As needed, extend ColumnMeta.filterVariant to support other types of filters\n console.warn(\n `column.getCanFilter() was true for column ID: ${column.id} but no filterControlFromProps was passed and component for filterVariant: ${column.columnDef.meta?.filterVariant} is not implemented`,\n )\n }\n return <></>\n }, [column, filterControlFromProps, title])\n\n const filterControl = getFilterControl()\n\n return (\n <Box\n sx={{\n display: 'flex',\n alignContent: 'center',\n alignItems: 'center',\n justifyContent: 'space-between',\n overflow: 'hidden',\n minWidth: 0,\n }}\n >\n <span\n style={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: wrap ? 'pre-wrap' : 'nowrap',\n minWidth: 0,\n }}\n title={title}\n >\n {title}\n </span>\n <Box\n role={'menubar'}\n sx={{\n display: 'flex',\n alignItems: 'center',\n height: '22px',\n ml: 2,\n gap: 0.25,\n }}\n >\n {helpText && (\n <Tooltip title={helpText} placement={'top'}>\n <IconButton size={'small'}>\n <HelpTwoTone fontSize={'inherit'} />\n </IconButton>\n </Tooltip>\n )}\n {column.getCanFilter() && filterControl}\n {column.getCanSort() && (\n <Tooltip title={`Sort by ${title}`} placement={'top'}>\n <IconButton\n role=\"button\"\n size={'small'}\n aria-label={`Sort by ${title}`}\n tabIndex={0}\n onKeyPress={() => column.toggleSorting()}\n onClick={() => column.toggleSorting()}\n >\n <IconSvg\n icon={column.getIsSorted() === 'asc' ? 'sortUp' : 'sortDown'}\n wrap={false}\n sx={{\n color: column.getIsSorted() ? 'primary.main' : 'grey.700',\n backgroundColor: 'none',\n }}\n />\n </IconButton>\n </Tooltip>\n )}\n {additionalButtons}\n </Box>\n </Box>\n )\n}\n"],"mappings":";;;;;;;AAqBA,SAAwB,EACtB,GACA;CACA,IAAM,EACJ,WACA,WAAQ,EAAM,OAAO,IACrB,UAAO,IACP,aACA,eAAe,GACf,yBACE,GAkBE,IAhBmB,QAAkB;AACzC,MAAI,EACF,QAAO;AAET,MAAI,EAAO,cAAc,EAAE;AACzB,OAAI,EAAO,UAAU,MAAM,kBAAkB,cAC3C,QAAO,kBAAC,GAAD;IAAgC;IAAe;IAAS,CAAA;AAGjE,WAAQ,KACN,iDAAiD,EAAO,GAAG,6EAA6E,EAAO,UAAU,MAAM,cAAc,qBAC9K;;AAEH,SAAO,kBAAA,GAAA,EAAK,CAAA;IACX;EAAC;EAAQ;EAAwB;EAAM,CAEpB,EAAkB;AAExC,QACE,kBAAC,GAAD;EACE,IAAI;GACF,SAAS;GACT,cAAc;GACd,YAAY;GACZ,gBAAgB;GAChB,UAAU;GACV,UAAU;GACX;YARH,CAUE,kBAAC,QAAD;GACE,OAAO;IACL,UAAU;IACV,cAAc;IACd,YAAY,IAAO,aAAa;IAChC,UAAU;IACX;GACM;aAEN;GACI,CAAA,EACP,kBAAC,GAAD;GACE,MAAM;GACN,IAAI;IACF,SAAS;IACT,YAAY;IACZ,QAAQ;IACR,IAAI;IACJ,KAAK;IACN;aARH;IAUG,KACC,kBAAC,GAAD;KAAS,OAAO;KAAU,WAAW;eACnC,kBAAC,GAAD;MAAY,MAAM;gBAChB,kBAAC,GAAD,EAAa,UAAU,WAAa,CAAA;MACzB,CAAA;KACL,CAAA;IAEX,EAAO,cAAc,IAAI;IACzB,EAAO,YAAY,IAClB,kBAAC,GAAD;KAAS,OAAO,WAAW;KAAS,WAAW;eAC7C,kBAAC,GAAD;MACE,MAAK;MACL,MAAM;MACN,cAAY,WAAW;MACvB,UAAU;MACV,kBAAkB,EAAO,eAAe;MACxC,eAAe,EAAO,eAAe;gBAErC,kBAAC,GAAD;OACE,MAAM,EAAO,aAAa,KAAK,QAAQ,WAAW;OAClD,MAAM;OACN,IAAI;QACF,OAAO,EAAO,aAAa,GAAG,iBAAiB;QAC/C,iBAAiB;QAClB;OACD,CAAA;MACS,CAAA;KACL,CAAA;IAEX;IACG;KACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColumnHeaderEnumFilter.js","names":[],"sources":["../../../src/components/TanStackTable/ColumnHeaderEnumFilter.tsx"],"sourcesContent":["import { Column, RowData } from '@tanstack/react-table'\nimport { isEmpty, isEqual, noop } from 'lodash-es'\nimport { useMemo } from 'react'\nimport EnumFacetFilterUI, {\n EnumFacetFilterUIProps,\n RenderedFacetValue,\n} from '../widgets/query-filter/EnumFacetFilter/EnumFacetFilterUI'\n\nfunction setEnumFilterValue<TData extends RowData, TValue = unknown>(\n column: Column<TData, TValue>,\n newValue: TValue,\n) {\n if (column.columnDef.meta?.enableMultipleSelect) {\n // Append the value to the list, or create the list if it does not exist\n column.setFilterValue((curr: TValue[] | null): TValue[] | null => {\n if (curr) {\n return [...curr, newValue]\n }\n return [newValue]\n })\n } else {\n // Replace the list\n column.setFilterValue([newValue])\n }\n}\n\nfunction removeEnumFilterValue<TData extends RowData, TValue = unknown>(\n column: Column<TData, TValue>,\n valueToRemove: TValue,\n) {\n column.setFilterValue((curr: TValue[] | null): TValue[] | null => {\n if (curr) {\n const newValue = [\n ...curr.filter(currValue => !isEqual(currValue, valueToRemove)),\n ]\n if (!isEmpty(newValue)) {\n return newValue\n }\n }\n return null\n })\n}\n\ntype ColumnHeaderEnumFilterProps<TData = unknown, TValue = unknown> = {\n column: Column<TData, TValue>\n title: string\n}\n\nfunction getMaybeFacetedUniqueValues<TValue = unknown>(\n columnFacetedUniqueValues: Map<TValue, number>,\n): {\n value: TValue\n count?: number\n}[] {\n if (columnFacetedUniqueValues) {\n // @tanstack/table currently has no API to define a unique set of values for a column without count statistics\n // In each instance, we manually defined a set of unique values and set the count to 0\n const hasFacetCounts = Array.from(columnFacetedUniqueValues.values()).some(\n v => v != 0,\n )\n return Array.from(columnFacetedUniqueValues.entries()).map(\n (entry: [TValue, number]) => ({\n value: entry[0],\n count: hasFacetCounts ? entry[1] : undefined,\n }),\n )\n }\n return []\n}\n\n/**\n * Used to create {@link EnumFacetFilterUIProps} that can be used to create a control to filter column values based on\n * the provided @tanstack/react-table {@link Column}. The custom properties used to configure the filter should be set\n * in the column's `meta` field.\n * @param props\n * @constructor\n */\nexport function ColumnHeaderEnumFilter<TData = unknown, TValue = unknown>(\n props: ColumnHeaderEnumFilterProps<TData, TValue>,\n) {\n type EnumFilterValue = TValue[]\n const { column, title } = props\n\n const canMultiSelect = column.columnDef.meta?.enableMultipleSelect || false\n\n const _uncheckedFilterValues = column.getFilterValue() as EnumFilterValue\n const filterValues = useMemo(\n () => _uncheckedFilterValues || [],\n [_uncheckedFilterValues],\n )\n\n // Use the column's enumValues and filterValues to create the facet value objects used by EnumFacetFilterUI\n const facetedUniqueValues = column.getFacetedUniqueValues() as Map<\n TValue,\n number\n >\n const transformedFilterValues: RenderedFacetValue<TValue>[] = useMemo(\n () =>\n getMaybeFacetedUniqueValues(facetedUniqueValues).map(\n (facetValue): RenderedFacetValue<TValue> => {\n let displayText: string = facetValue.value as unknown as string\n if (column.columnDef?.meta?.getDisplayText) {\n displayText = column.columnDef.meta.getDisplayText(facetValue.value)\n }\n const currentFilterValue = filterValues || []\n const isSelected = Boolean(\n currentFilterValue.find(\n filterValue => filterValue === facetValue.value,\n ),\n )\n return { ...facetValue, displayText, isSelected }\n },\n ),\n [column, filterValues, facetedUniqueValues],\n )\n return (\n <EnumFacetFilterUI\n facetTitle={title}\n facetValues={transformedFilterValues}\n filterIsActive={column.getIsFiltered()}\n containerAs={'Dropdown'}\n dropdownType={'Icon'}\n defaultShowAllValues={true}\n onAddValueToSelection={newFilterValue => {\n setEnumFilterValue(column, newFilterValue)\n }}\n onRemoveValueFromSelection={valueToRemove => {\n removeEnumFilterValue(column, valueToRemove)\n }}\n onRemoveAllFacetSelections={() => {\n column.setFilterValue(null)\n }}\n onHoverOverValue={noop}\n canMultiSelect={canMultiSelect}\n />\n )\n}\n"],"mappings":";;;;;AAQA,SAAS,EACP,GACA,GACA;AACA,CAAI,EAAO,UAAU,MAAM,uBAEzB,EAAO,gBAAgB,MACjB,IACK,CAAC,GAAG,GAAM,EAAS,GAErB,CAAC,EAAS,CACjB,GAGF,EAAO,eAAe,CAAC,EAAS,CAAC;;AAIrC,SAAS,EACP,GACA,GACA;AACA,GAAO,gBAAgB,MAA2C;AAChE,MAAI,GAAM;GACR,IAAM,IAAW,CACf,GAAG,EAAK,QAAO,MAAa,CAAC,EAAQ,GAAW,EAAc,CAAC,CAChE;AACD,OAAI,CAAC,EAAQ,EAAS,CACpB,QAAO;;AAGX,SAAO;GACP;;AAQJ,SAAS,EACP,GAIE;AACF,KAAI,GAA2B;EAG7B,IAAM,IAAiB,MAAM,KAAK,EAA0B,QAAQ,CAAC,CAAC,MACpE,MAAK,KAAK,EACX;AACD,SAAO,MAAM,KAAK,EAA0B,SAAS,CAAC,CAAC,KACpD,OAA6B;GAC5B,OAAO,EAAM;GACb,OAAO,IAAiB,EAAM,KAAK,KAAA;GACpC,EACF;;AAEH,QAAO,EAAE;;AAUX,SAAgB,EACd,GACA;CAEA,IAAM,EAAE,WAAQ,aAAU,GAEpB,IAAiB,EAAO,UAAU,MAAM,wBAAwB,IAEhE,IAAyB,EAAO,gBAAgB,EAChD,IAAe,QACb,KAA0B,EAAE,EAClC,CAAC,EAAuB,CACzB,EAGK,IAAsB,EAAO,wBAAwB;AAuB3D,QACE,kBAAC,GAAD;EACE,YAAY;EACZ,aAtB0D,QAE1D,EAA4B,EAAoB,CAAC,KAC9C,MAA2C;GAC1C,IAAI,IAAsB,EAAW;AACrC,GAAI,EAAO,WAAW,MAAM,mBAC1B,IAAc,EAAO,UAAU,KAAK,eAAe,EAAW,MAAM;GAGtE,IAAM,IAAa,GADQ,KAAgB,EAAE,EAExB,MACjB,MAAe,MAAgB,EAAW,MAC3C;AAEH,UAAO;IAAE,GAAG;IAAY;IAAa;IAAY;IAEpD,EACH;GAAC;GAAQ;GAAc;GAAoB,
|
|
1
|
+
{"version":3,"file":"ColumnHeaderEnumFilter.js","names":[],"sources":["../../../src/components/TanStackTable/ColumnHeaderEnumFilter.tsx"],"sourcesContent":["import { Column, RowData } from '@tanstack/react-table'\nimport { isEmpty, isEqual, noop } from 'lodash-es'\nimport { useMemo } from 'react'\nimport EnumFacetFilterUI, {\n EnumFacetFilterUIProps,\n RenderedFacetValue,\n} from '../widgets/query-filter/EnumFacetFilter/EnumFacetFilterUI'\n\nfunction setEnumFilterValue<TData extends RowData, TValue = unknown>(\n column: Column<TData, TValue>,\n newValue: TValue,\n) {\n if (column.columnDef.meta?.enableMultipleSelect) {\n // Append the value to the list, or create the list if it does not exist\n column.setFilterValue((curr: TValue[] | null): TValue[] | null => {\n if (curr) {\n return [...curr, newValue]\n }\n return [newValue]\n })\n } else {\n // Replace the list\n column.setFilterValue([newValue])\n }\n}\n\nfunction removeEnumFilterValue<TData extends RowData, TValue = unknown>(\n column: Column<TData, TValue>,\n valueToRemove: TValue,\n) {\n column.setFilterValue((curr: TValue[] | null): TValue[] | null => {\n if (curr) {\n const newValue = [\n ...curr.filter(currValue => !isEqual(currValue, valueToRemove)),\n ]\n if (!isEmpty(newValue)) {\n return newValue\n }\n }\n return null\n })\n}\n\ntype ColumnHeaderEnumFilterProps<TData = unknown, TValue = unknown> = {\n column: Column<TData, TValue>\n title: string\n}\n\nfunction getMaybeFacetedUniqueValues<TValue = unknown>(\n columnFacetedUniqueValues: Map<TValue, number>,\n): {\n value: TValue\n count?: number\n}[] {\n if (columnFacetedUniqueValues) {\n // @tanstack/table currently has no API to define a unique set of values for a column without count statistics\n // In each instance, we manually defined a set of unique values and set the count to 0\n const hasFacetCounts = Array.from(columnFacetedUniqueValues.values()).some(\n v => v != 0,\n )\n return Array.from(columnFacetedUniqueValues.entries()).map(\n (entry: [TValue, number]) => ({\n value: entry[0],\n count: hasFacetCounts ? entry[1] : undefined,\n }),\n )\n }\n return []\n}\n\n/**\n * Used to create {@link EnumFacetFilterUIProps} that can be used to create a control to filter column values based on\n * the provided @tanstack/react-table {@link Column}. The custom properties used to configure the filter should be set\n * in the column's `meta` field.\n * @param props\n * @constructor\n */\nexport function ColumnHeaderEnumFilter<TData = unknown, TValue = unknown>(\n props: ColumnHeaderEnumFilterProps<TData, TValue>,\n) {\n type EnumFilterValue = TValue[]\n const { column, title } = props\n\n const canMultiSelect = column.columnDef.meta?.enableMultipleSelect || false\n\n const _uncheckedFilterValues = column.getFilterValue() as EnumFilterValue\n const filterValues = useMemo(\n () => _uncheckedFilterValues || [],\n [_uncheckedFilterValues],\n )\n\n // Use the column's enumValues and filterValues to create the facet value objects used by EnumFacetFilterUI\n const facetedUniqueValues = column.getFacetedUniqueValues() as Map<\n TValue,\n number\n >\n const transformedFilterValues: RenderedFacetValue<TValue>[] = useMemo(\n () =>\n getMaybeFacetedUniqueValues(facetedUniqueValues).map(\n (facetValue): RenderedFacetValue<TValue> => {\n let displayText: string = facetValue.value as unknown as string\n if (column.columnDef?.meta?.getDisplayText) {\n displayText = column.columnDef.meta.getDisplayText(facetValue.value)\n }\n const currentFilterValue = filterValues || []\n const isSelected = Boolean(\n currentFilterValue.find(\n filterValue => filterValue === facetValue.value,\n ),\n )\n return { ...facetValue, displayText, isSelected }\n },\n ),\n [column, filterValues, facetedUniqueValues],\n )\n return (\n <EnumFacetFilterUI\n facetTitle={title}\n facetValues={transformedFilterValues}\n filterIsActive={column.getIsFiltered()}\n containerAs={'Dropdown'}\n dropdownType={'Icon'}\n defaultShowAllValues={true}\n onAddValueToSelection={newFilterValue => {\n setEnumFilterValue(column, newFilterValue)\n }}\n onRemoveValueFromSelection={valueToRemove => {\n removeEnumFilterValue(column, valueToRemove)\n }}\n onRemoveAllFacetSelections={() => {\n column.setFilterValue(null)\n }}\n onHoverOverValue={noop}\n canMultiSelect={canMultiSelect}\n />\n )\n}\n"],"mappings":";;;;;AAQA,SAAS,EACP,GACA,GACA;AACA,CAAI,EAAO,UAAU,MAAM,uBAEzB,EAAO,gBAAgB,MACjB,IACK,CAAC,GAAG,GAAM,EAAS,GAErB,CAAC,EAAS,CACjB,GAGF,EAAO,eAAe,CAAC,EAAS,CAAC;;AAIrC,SAAS,EACP,GACA,GACA;AACA,GAAO,gBAAgB,MAA2C;AAChE,MAAI,GAAM;GACR,IAAM,IAAW,CACf,GAAG,EAAK,QAAO,MAAa,CAAC,EAAQ,GAAW,EAAc,CAAC,CAChE;AACD,OAAI,CAAC,EAAQ,EAAS,CACpB,QAAO;;AAGX,SAAO;GACP;;AAQJ,SAAS,EACP,GAIE;AACF,KAAI,GAA2B;EAG7B,IAAM,IAAiB,MAAM,KAAK,EAA0B,QAAQ,CAAC,CAAC,MACpE,MAAK,KAAK,EACX;AACD,SAAO,MAAM,KAAK,EAA0B,SAAS,CAAC,CAAC,KACpD,OAA6B;GAC5B,OAAO,EAAM;GACb,OAAO,IAAiB,EAAM,KAAK,KAAA;GACpC,EACF;;AAEH,QAAO,EAAE;;AAUX,SAAgB,EACd,GACA;CAEA,IAAM,EAAE,WAAQ,aAAU,GAEpB,IAAiB,EAAO,UAAU,MAAM,wBAAwB,IAEhE,IAAyB,EAAO,gBAAgB,EAChD,IAAe,QACb,KAA0B,EAAE,EAClC,CAAC,EAAuB,CACzB,EAGK,IAAsB,EAAO,wBAAwB;AAuB3D,QACE,kBAAC,GAAD;EACE,YAAY;EACZ,aAtB0D,QAE1D,EAA4B,EAAoB,CAAC,KAC9C,MAA2C;GAC1C,IAAI,IAAsB,EAAW;AACrC,GAAI,EAAO,WAAW,MAAM,mBAC1B,IAAc,EAAO,UAAU,KAAK,eAAe,EAAW,MAAM;GAGtE,IAAM,IAAa,GADQ,KAAgB,EAAE,EAExB,MACjB,MAAe,MAAgB,EAAW,MAC3C;AAEH,UAAO;IAAE,GAAG;IAAY;IAAa;IAAY;IAEpD,EACH;GAAC;GAAQ;GAAc;GAAoB,CAK5B;EACb,gBAAgB,EAAO,eAAe;EACtC,aAAa;EACb,cAAc;EACd,sBAAsB;EACtB,wBAAuB,MAAkB;AACvC,KAAmB,GAAQ,EAAe;;EAE5C,6BAA4B,MAAiB;AAC3C,KAAsB,GAAQ,EAAc;;EAE9C,kCAAkC;AAChC,KAAO,eAAe,KAAK;;EAE7B,kBAAkB;EACF;EAChB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableBody.js","names":[],"sources":["../../../src/components/TanStackTable/TableBody.tsx"],"sourcesContent":["import { getSlotProps } from '@/utils/slots/SlotUtils'\nimport { styled } from '@mui/material'\nimport { Row, Table } from '@tanstack/react-table'\nimport { identity } from 'lodash-es'\nimport { memo } from 'react'\nimport { TableCellRenderer as DefaultTableCellRenderer } from './TableCellRenderer'\nimport {\n TableBodyPropsRowOverride,\n TableBodySlotProps,\n TableBodySlots,\n TrOwnerState,\n} from './types'\n\n// Simple wrapper around 'tr' to prevent forwarding invalid HTML attributes\nconst TableRow = styled('tr', {\n shouldForwardProp: prop => prop !== 'row' && prop !== 'tableRow',\n})({})\n\nexport type TableBodyProps<TData = unknown, TRowType = Row<TData>> = {\n /** The table instance */\n table: Table<TData>\n slots?: TableBodySlots<TData, TRowType>\n slotProps?: TableBodySlotProps<TData, TRowType>\n} & TableBodyPropsRowOverride<TData, TRowType>\n\n/**\n * A table body component for use with @tanstack/react-table. This component renders the rows of the table.\n * @param props\n * @constructor\n */\nexport function TableBody<TData = unknown, TRowType = Row<TData>>(\n props: TableBodyProps<TData, TRowType>,\n) {\n const { table, slots = {}, slotProps = {} } = props\n\n // By default, use TanStack Table Rows and the identity function as a transform.\n // This can be overridden e.g. to accomplish row virtualization.\n const {\n rows = table.getRowModel().rows as TRowType[],\n rowTransform = identity,\n } = props\n\n const {\n Tbody = 'tbody',\n Tr = TableRow,\n TableCellRenderer = DefaultTableCellRenderer<TData>,\n } = slots\n const { Tbody: tbodySlotProps = {}, Tr: _trSlotProps = {} } = slotProps\n\n return (\n <Tbody {...tbodySlotProps}>\n {rows.map((row, index) => {\n const tableRow: Row<TData> | undefined = rowTransform(row)\n\n const trOwnerState: TrOwnerState<TData, TRowType> = { row, tableRow }\n const trSlotProps = getSlotProps(_trSlotProps, trOwnerState)\n\n let mergedClassNames = trSlotProps.className ?? ''\n if (table.options.meta?.getRowClassNames) {\n mergedClassNames =\n `${mergedClassNames} ${table.options.meta.getRowClassNames(\n row,\n )}`.trim()\n }\n\n return (\n <Tr\n key={tableRow?.id ?? index}\n {...trSlotProps}\n row={row}\n tableRow={tableRow}\n className={mergedClassNames}\n >\n {tableRow?.getVisibleCells().map((props, index) => (\n <TableCellRenderer key={index} {...props} />\n ))}\n </Tr>\n )\n })}\n </Tbody>\n )\n}\n\n// special memoized wrapper for our table body that we will use during column resizing\n// see https://tanstack.com/table/v8/docs/guide/column-sizing#advanced-column-resizing-performance\nexport const MemoizedTableBody = memo(TableBody, (prev, next) => {\n return prev.table.options.data === next.table.options.data\n}) as typeof TableBody\n"],"mappings":";;;;;;;AAcA,IAAM,IAAW,EAAO,MAAM,EAC5B,oBAAmB,MAAQ,MAAS,SAAS,MAAS,YACvD,CAAC,CAAC,EAAE,CAAC;AAcN,SAAgB,EACd,GACA;CACA,IAAM,EAAE,UAAO,WAAQ,EAAE,EAAE,eAAY,EAAE,KAAK,GAIxC,EACJ,UAAO,EAAM,aAAa,CAAC,MAC3B,kBAAe,MACb,GAEE,EACJ,WAAQ,SACR,QAAK,GACL,mBAAA,IAAoB,MAClB,GACE,EAAE,OAAO,IAAiB,EAAE,EAAE,IAAI,IAAe,EAAE,KAAK;AAE9D,QACE,kBAAC,GAAD;EAAO,GAAI;YACR,EAAK,KAAK,GAAK,MAAU;GACxB,IAAM,IAAmC,EAAa,EAAI,EAGpD,IAAc,EAAa,
|
|
1
|
+
{"version":3,"file":"TableBody.js","names":[],"sources":["../../../src/components/TanStackTable/TableBody.tsx"],"sourcesContent":["import { getSlotProps } from '@/utils/slots/SlotUtils'\nimport { styled } from '@mui/material'\nimport { Row, Table } from '@tanstack/react-table'\nimport { identity } from 'lodash-es'\nimport { memo } from 'react'\nimport { TableCellRenderer as DefaultTableCellRenderer } from './TableCellRenderer'\nimport {\n TableBodyPropsRowOverride,\n TableBodySlotProps,\n TableBodySlots,\n TrOwnerState,\n} from './types'\n\n// Simple wrapper around 'tr' to prevent forwarding invalid HTML attributes\nconst TableRow = styled('tr', {\n shouldForwardProp: prop => prop !== 'row' && prop !== 'tableRow',\n})({})\n\nexport type TableBodyProps<TData = unknown, TRowType = Row<TData>> = {\n /** The table instance */\n table: Table<TData>\n slots?: TableBodySlots<TData, TRowType>\n slotProps?: TableBodySlotProps<TData, TRowType>\n} & TableBodyPropsRowOverride<TData, TRowType>\n\n/**\n * A table body component for use with @tanstack/react-table. This component renders the rows of the table.\n * @param props\n * @constructor\n */\nexport function TableBody<TData = unknown, TRowType = Row<TData>>(\n props: TableBodyProps<TData, TRowType>,\n) {\n const { table, slots = {}, slotProps = {} } = props\n\n // By default, use TanStack Table Rows and the identity function as a transform.\n // This can be overridden e.g. to accomplish row virtualization.\n const {\n rows = table.getRowModel().rows as TRowType[],\n rowTransform = identity,\n } = props\n\n const {\n Tbody = 'tbody',\n Tr = TableRow,\n TableCellRenderer = DefaultTableCellRenderer<TData>,\n } = slots\n const { Tbody: tbodySlotProps = {}, Tr: _trSlotProps = {} } = slotProps\n\n return (\n <Tbody {...tbodySlotProps}>\n {rows.map((row, index) => {\n const tableRow: Row<TData> | undefined = rowTransform(row)\n\n const trOwnerState: TrOwnerState<TData, TRowType> = { row, tableRow }\n const trSlotProps = getSlotProps(_trSlotProps, trOwnerState)\n\n let mergedClassNames = trSlotProps.className ?? ''\n if (table.options.meta?.getRowClassNames) {\n mergedClassNames =\n `${mergedClassNames} ${table.options.meta.getRowClassNames(\n row,\n )}`.trim()\n }\n\n return (\n <Tr\n key={tableRow?.id ?? index}\n {...trSlotProps}\n row={row}\n tableRow={tableRow}\n className={mergedClassNames}\n >\n {tableRow?.getVisibleCells().map((props, index) => (\n <TableCellRenderer key={index} {...props} />\n ))}\n </Tr>\n )\n })}\n </Tbody>\n )\n}\n\n// special memoized wrapper for our table body that we will use during column resizing\n// see https://tanstack.com/table/v8/docs/guide/column-sizing#advanced-column-resizing-performance\nexport const MemoizedTableBody = memo(TableBody, (prev, next) => {\n return prev.table.options.data === next.table.options.data\n}) as typeof TableBody\n"],"mappings":";;;;;;;AAcA,IAAM,IAAW,EAAO,MAAM,EAC5B,oBAAmB,MAAQ,MAAS,SAAS,MAAS,YACvD,CAAC,CAAC,EAAE,CAAC;AAcN,SAAgB,EACd,GACA;CACA,IAAM,EAAE,UAAO,WAAQ,EAAE,EAAE,eAAY,EAAE,KAAK,GAIxC,EACJ,UAAO,EAAM,aAAa,CAAC,MAC3B,kBAAe,MACb,GAEE,EACJ,WAAQ,SACR,QAAK,GACL,mBAAA,IAAoB,MAClB,GACE,EAAE,OAAO,IAAiB,EAAE,EAAE,IAAI,IAAe,EAAE,KAAK;AAE9D,QACE,kBAAC,GAAD;EAAO,GAAI;YACR,EAAK,KAAK,GAAK,MAAU;GACxB,IAAM,IAAmC,EAAa,EAAI,EAGpD,IAAc,EAAa,GAAc;IADO;IAAK;IACZ,CAAa,EAExD,IAAmB,EAAY,aAAa;AAQhD,UAPI,EAAM,QAAQ,MAAM,qBACtB,IACE,GAAG,EAAiB,GAAG,EAAM,QAAQ,KAAK,iBACxC,EACD,GAAG,MAAM,GAIZ,kBAAC,GAAD;IAEE,GAAI;IACC;IACK;IACV,WAAW;cAEV,GAAU,iBAAiB,CAAC,KAAK,GAAO,MACvC,kBAAC,GAAD,EAA+B,GAAI,GAAS,EAApB,EAAoB,CAC5C;IACC,EATE,GAAU,MAAM,EASlB;IAEP;EACI,CAAA;;AAMZ,IAAa,IAAoB,EAAK,IAAY,GAAM,MAC/C,EAAK,MAAM,QAAQ,SAAS,EAAK,MAAM,QAAQ,KACtD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamSubjectsSelector.js","names":[],"sources":["../../../src/components/TeamSubjectsSelector/TeamSubjectsSelector.tsx"],"sourcesContent":["import { AddCircleTwoTone, HelpOutlineTwoTone } from '@mui/icons-material'\nimport {\n Alert,\n Box,\n Button,\n IconButton,\n InputLabel,\n Stack,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport {\n RestrictableObjectDescriptor,\n RestrictableObjectType,\n} from '@sage-bionetworks/synapse-types'\nimport { noop } from 'lodash-es'\nimport { useMemo, useState } from 'react'\nimport IconSvg from '../IconSvg'\nimport { TeamBadgeOrError } from './TeamBadgeOrError'\n\nexport type TeamSubjectsSelectorProps = {\n // will be filtered to only team subjects\n subjects: RestrictableObjectDescriptor[]\n onUpdate: (subjects: RestrictableObjectDescriptor[]) => void\n onUpdateTeamIDsTextbox?: (value: string) => void // called when the team IDs text box is updated\n}\n\nexport const TEAM_ALREADY_SELECTED = (teamId: string) =>\n `Team ${teamId} has already been added to this AR.`\nexport const TEAM_PARSING_ERROR = (teamId: string) =>\n `Parsing errors encountered. Invalid Team ID: ${teamId}`\nexport const NO_TEAMS_SELECTED = 'No teams selected.'\nexport const REMOVE_TEXT = 'Remove from Access Requirement'\nexport const HELP_TEXT = 'Enter Team IDs (i.e. 123, 456)'\nconst teamIdsStringDefault = ''\n\nfunction TeamSubjectsSelector(props: TeamSubjectsSelectorProps) {\n const { subjects, onUpdate, onUpdateTeamIDsTextbox = noop } = props\n const [teamIdsString, setTeamIdsString] =\n useState<string>(teamIdsStringDefault)\n const [error, setError] = useState<string | null>(null)\n\n const teamSubjects = useMemo(() => {\n return subjects.filter(subject => {\n return subject.type === RestrictableObjectType.TEAM ? subject : null\n })\n }, [subjects])\n\n const onChangeTeamIdsTextbox = (value: string) => {\n setTeamIdsString(value)\n onUpdateTeamIDsTextbox(value)\n }\n\n const onRemove = (subjectId: string) => {\n const updatedSubjects = teamSubjects.filter(subject => {\n return subject.id !== subjectId\n })\n onUpdate(updatedSubjects)\n }\n\n const onAddTeams = (teamIdsString: string | null) => {\n const teamIds = teamIdsString?.split(',').map(item => item.trim())\n const dedupedTeamIds = [...new Set(teamIds)]\n if (dedupedTeamIds && dedupedTeamIds.length > 0) {\n const updatedTeamSubjects = [...teamSubjects]\n for (const teamId of dedupedTeamIds) {\n const validId = /^\\d+$/.test(teamId)\n const alreadyAdded = teamSubjects.some(subject => subject.id === teamId)\n if (!validId) {\n setError(TEAM_PARSING_ERROR(teamId))\n return\n } else if (alreadyAdded) {\n setError(TEAM_ALREADY_SELECTED(teamId))\n return\n } else {\n const newSubject: RestrictableObjectDescriptor = {\n id: teamId,\n type: RestrictableObjectType.TEAM,\n }\n updatedTeamSubjects.push(newSubject)\n }\n }\n onChangeTeamIdsTextbox(teamIdsStringDefault)\n onUpdate(updatedTeamSubjects)\n }\n setError(null)\n }\n\n return (\n <>\n <Box\n sx={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '0px 15px',\n pb: '10px',\n }}\n >\n {teamSubjects.length === 0 && (\n <Typography\n variant=\"body1Italic\"\n sx={{\n pb: 2,\n }}\n >\n {NO_TEAMS_SELECTED}\n </Typography>\n )}\n {teamSubjects.map(subject => {\n return (\n <Stack\n key={subject.id}\n direction=\"row\"\n data-testid=\"selected-team\"\n sx={{\n alignItems: 'center',\n pb: 1,\n }}\n >\n <TeamBadgeOrError teamId={subject.id} />\n <IconButton\n aria-label={REMOVE_TEXT}\n onClick={() => onRemove(subject.id)}\n sx={{\n '&:hover': {\n color: 'error.main',\n },\n }}\n >\n <IconSvg icon={'delete'} fontSize={'inherit'} wrap={false} />\n </IconButton>\n </Stack>\n )\n })}\n </Box>\n <InputLabel htmlFor=\"teamIDs\">Add Team IDs</InputLabel>\n <Stack\n direction=\"row\"\n sx={{\n gap: 1,\n alignItems: 'center',\n mb: 2,\n }}\n >\n <TextField\n id=\"teamIDs\"\n name=\"teamIDs\"\n placeholder={HELP_TEXT}\n value={teamIdsString}\n onChange={event => onChangeTeamIdsTextbox(event.target.value)}\n fullWidth\n />\n <Button\n startIcon={<AddCircleTwoTone />}\n variant=\"outlined\"\n sx={{ flexShrink: 0, height: '53px' }}\n onClick={() => onAddTeams(teamIdsString)}\n disabled={teamIdsString === ''}\n >\n Add Teams\n </Button>\n <Tooltip title={HELP_TEXT} placement=\"right\">\n <HelpOutlineTwoTone sx={{ color: 'grey.600' }} />\n </Tooltip>\n </Stack>\n {error && <Alert severity=\"warning\">{error}</Alert>}\n </>\n )\n}\n\nexport default TeamSubjectsSelector\n"],"mappings":";;;;;;;;;AA4BA,IAAa,KAAyB,MACpC,QAAQ,EAAO,sCACJ,KAAsB,MACjC,gDAAgD,KACrC,IAAoB,sBACpB,IAAc,kCACd,IAAY,kCACnB,IAAuB;AAE7B,SAAS,EAAqB,GAAkC;CAC9D,IAAM,EAAE,aAAU,aAAU,4BAAyB,MAAS,GACxD,CAAC,GAAe,KACpB,EAAiB,EAAqB,EAClC,CAAC,GAAO,KAAY,EAAwB,KAAK,EAEjD,IAAe,QACZ,EAAS,QAAO,MACd,EAAQ,SAAS,EAAuB,OAAO,IAAU,KAChE,EACD,CAAC,EAAS,CAAC,EAER,KAA0B,MAAkB;AAEhD,EADA,EAAiB,EAAM,EACvB,EAAuB,EAAM;IAGzB,KAAY,MAAsB;AAItC,IAHwB,EAAa,QAAO,MACnC,EAAQ,OAAO,
|
|
1
|
+
{"version":3,"file":"TeamSubjectsSelector.js","names":[],"sources":["../../../src/components/TeamSubjectsSelector/TeamSubjectsSelector.tsx"],"sourcesContent":["import { AddCircleTwoTone, HelpOutlineTwoTone } from '@mui/icons-material'\nimport {\n Alert,\n Box,\n Button,\n IconButton,\n InputLabel,\n Stack,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport {\n RestrictableObjectDescriptor,\n RestrictableObjectType,\n} from '@sage-bionetworks/synapse-types'\nimport { noop } from 'lodash-es'\nimport { useMemo, useState } from 'react'\nimport IconSvg from '../IconSvg'\nimport { TeamBadgeOrError } from './TeamBadgeOrError'\n\nexport type TeamSubjectsSelectorProps = {\n // will be filtered to only team subjects\n subjects: RestrictableObjectDescriptor[]\n onUpdate: (subjects: RestrictableObjectDescriptor[]) => void\n onUpdateTeamIDsTextbox?: (value: string) => void // called when the team IDs text box is updated\n}\n\nexport const TEAM_ALREADY_SELECTED = (teamId: string) =>\n `Team ${teamId} has already been added to this AR.`\nexport const TEAM_PARSING_ERROR = (teamId: string) =>\n `Parsing errors encountered. Invalid Team ID: ${teamId}`\nexport const NO_TEAMS_SELECTED = 'No teams selected.'\nexport const REMOVE_TEXT = 'Remove from Access Requirement'\nexport const HELP_TEXT = 'Enter Team IDs (i.e. 123, 456)'\nconst teamIdsStringDefault = ''\n\nfunction TeamSubjectsSelector(props: TeamSubjectsSelectorProps) {\n const { subjects, onUpdate, onUpdateTeamIDsTextbox = noop } = props\n const [teamIdsString, setTeamIdsString] =\n useState<string>(teamIdsStringDefault)\n const [error, setError] = useState<string | null>(null)\n\n const teamSubjects = useMemo(() => {\n return subjects.filter(subject => {\n return subject.type === RestrictableObjectType.TEAM ? subject : null\n })\n }, [subjects])\n\n const onChangeTeamIdsTextbox = (value: string) => {\n setTeamIdsString(value)\n onUpdateTeamIDsTextbox(value)\n }\n\n const onRemove = (subjectId: string) => {\n const updatedSubjects = teamSubjects.filter(subject => {\n return subject.id !== subjectId\n })\n onUpdate(updatedSubjects)\n }\n\n const onAddTeams = (teamIdsString: string | null) => {\n const teamIds = teamIdsString?.split(',').map(item => item.trim())\n const dedupedTeamIds = [...new Set(teamIds)]\n if (dedupedTeamIds && dedupedTeamIds.length > 0) {\n const updatedTeamSubjects = [...teamSubjects]\n for (const teamId of dedupedTeamIds) {\n const validId = /^\\d+$/.test(teamId)\n const alreadyAdded = teamSubjects.some(subject => subject.id === teamId)\n if (!validId) {\n setError(TEAM_PARSING_ERROR(teamId))\n return\n } else if (alreadyAdded) {\n setError(TEAM_ALREADY_SELECTED(teamId))\n return\n } else {\n const newSubject: RestrictableObjectDescriptor = {\n id: teamId,\n type: RestrictableObjectType.TEAM,\n }\n updatedTeamSubjects.push(newSubject)\n }\n }\n onChangeTeamIdsTextbox(teamIdsStringDefault)\n onUpdate(updatedTeamSubjects)\n }\n setError(null)\n }\n\n return (\n <>\n <Box\n sx={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '0px 15px',\n pb: '10px',\n }}\n >\n {teamSubjects.length === 0 && (\n <Typography\n variant=\"body1Italic\"\n sx={{\n pb: 2,\n }}\n >\n {NO_TEAMS_SELECTED}\n </Typography>\n )}\n {teamSubjects.map(subject => {\n return (\n <Stack\n key={subject.id}\n direction=\"row\"\n data-testid=\"selected-team\"\n sx={{\n alignItems: 'center',\n pb: 1,\n }}\n >\n <TeamBadgeOrError teamId={subject.id} />\n <IconButton\n aria-label={REMOVE_TEXT}\n onClick={() => onRemove(subject.id)}\n sx={{\n '&:hover': {\n color: 'error.main',\n },\n }}\n >\n <IconSvg icon={'delete'} fontSize={'inherit'} wrap={false} />\n </IconButton>\n </Stack>\n )\n })}\n </Box>\n <InputLabel htmlFor=\"teamIDs\">Add Team IDs</InputLabel>\n <Stack\n direction=\"row\"\n sx={{\n gap: 1,\n alignItems: 'center',\n mb: 2,\n }}\n >\n <TextField\n id=\"teamIDs\"\n name=\"teamIDs\"\n placeholder={HELP_TEXT}\n value={teamIdsString}\n onChange={event => onChangeTeamIdsTextbox(event.target.value)}\n fullWidth\n />\n <Button\n startIcon={<AddCircleTwoTone />}\n variant=\"outlined\"\n sx={{ flexShrink: 0, height: '53px' }}\n onClick={() => onAddTeams(teamIdsString)}\n disabled={teamIdsString === ''}\n >\n Add Teams\n </Button>\n <Tooltip title={HELP_TEXT} placement=\"right\">\n <HelpOutlineTwoTone sx={{ color: 'grey.600' }} />\n </Tooltip>\n </Stack>\n {error && <Alert severity=\"warning\">{error}</Alert>}\n </>\n )\n}\n\nexport default TeamSubjectsSelector\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.js","names":[],"sources":["../../../src/components/TextField/TextField.tsx"],"sourcesContent":["import StyledFormControl from '@/components/styled/StyledFormControl'\nimport {\n Box,\n InputBase,\n InputLabel,\n TextFieldProps as MuiTextFieldProps,\n Typography,\n} from '@mui/material'\nimport styles from './TextField.module.scss'\nimport { PropsWithChildren, useId, useMemo } from 'react'\n\nexport type TextFieldProps = Pick<\n MuiTextFieldProps,\n | 'autoComplete'\n | 'autoFocus'\n | 'disabled'\n | 'error'\n | 'fullWidth'\n | 'helperText'\n | 'id'\n | 'inputProps'\n | 'label'\n | 'maxRows'\n | 'minRows'\n | 'multiline'\n | 'onBlur'\n | 'onChange'\n | 'placeholder'\n | 'required'\n | 'rows'\n | 'sx'\n | 'type'\n | 'value'\n> & { noWrapInFormControl?: boolean; maxCharacterCount?: number }\n\n/**\n * A styled text field built using MUI components and designed to match the Sage Design System (SDS) input fields.\n */\nexport default function TextField(props: TextFieldProps) {\n const id = useId()\n const {\n noWrapInFormControl,\n label: _label,\n helperText,\n maxCharacterCount,\n inputProps: inputPropsProp,\n ...rest\n } = props\n const mergedInputProps = maxCharacterCount\n ? { ...inputPropsProp, maxLength: maxCharacterCount }\n : inputPropsProp\n const currentLength = typeof rest.value === 'string' ? rest.value.length : 0\n const Wrapper = useMemo(\n () =>\n noWrapInFormControl\n ? (props: PropsWithChildren<object>) => <>{props.children}</>\n : (props: PropsWithChildren<object>) => (\n <StyledFormControl fullWidth sx={{ my: 1 }}>\n {props.children}\n </StyledFormControl>\n ),\n [noWrapInFormControl],\n )\n return (\n <Wrapper>\n {(props.label || helperText) && (\n <Box className={styles.labelRow}>\n {props.label && (\n <InputLabel\n htmlFor={props.id || id}\n className={styles.inputLabel}\n sx={{\n '&::after': ({ palette }) => ({\n content: props.required ? '\"*\"' : undefined,\n marginLeft: '3px',\n color: palette.secondary.main,\n }),\n }}\n >\n {props.label}\n </InputLabel>\n )}\n {helperText && (\n <Typography variant=\"body2\" className={styles.helperText}>\n {helperText}\n </Typography>\n )}\n </Box>\n )}\n <InputBase id={id} inputProps={mergedInputProps} {...rest}></InputBase>\n {maxCharacterCount !== undefined && (\n <Typography variant=\"body2\" className={styles.charCount}>\n {currentLength}/{maxCharacterCount}\n </Typography>\n )}\n </Wrapper>\n )\n}\n"],"mappings":";;;;;;AAsCA,SAAwB,EAAU,GAAuB;CACvD,IAAM,IAAK,GAAO,EACZ,EACJ,wBACA,OAAO,GACP,eACA,sBACA,YAAY,GACZ,GAAG,MACD,GACE,IAAmB,IACrB;EAAE,GAAG;EAAgB,WAAW;EAAmB,GACnD,GACE,IAAgB,OAAO,EAAK,SAAU,WAAW,EAAK,MAAM,SAAS;AAY3E,QACE,kBAZc,QAEZ,KACK,MAAqC,kBAAA,GAAA,EAAA,UAAG,EAAM,UAAY,CAAA,IAC1D,MACC,kBAAC,GAAD;EAAmB,WAAA;EAAU,IAAI,EAAE,IAAI,GAAG;YACvC,EAAM;EACW,CAAA,EAE5B,CAAC,EAAoB,
|
|
1
|
+
{"version":3,"file":"TextField.js","names":[],"sources":["../../../src/components/TextField/TextField.tsx"],"sourcesContent":["import StyledFormControl from '@/components/styled/StyledFormControl'\nimport {\n Box,\n InputBase,\n InputLabel,\n TextFieldProps as MuiTextFieldProps,\n Typography,\n} from '@mui/material'\nimport styles from './TextField.module.scss'\nimport { PropsWithChildren, useId, useMemo } from 'react'\n\nexport type TextFieldProps = Pick<\n MuiTextFieldProps,\n | 'autoComplete'\n | 'autoFocus'\n | 'disabled'\n | 'error'\n | 'fullWidth'\n | 'helperText'\n | 'id'\n | 'inputProps'\n | 'label'\n | 'maxRows'\n | 'minRows'\n | 'multiline'\n | 'onBlur'\n | 'onChange'\n | 'placeholder'\n | 'required'\n | 'rows'\n | 'sx'\n | 'type'\n | 'value'\n> & { noWrapInFormControl?: boolean; maxCharacterCount?: number }\n\n/**\n * A styled text field built using MUI components and designed to match the Sage Design System (SDS) input fields.\n */\nexport default function TextField(props: TextFieldProps) {\n const id = useId()\n const {\n noWrapInFormControl,\n label: _label,\n helperText,\n maxCharacterCount,\n inputProps: inputPropsProp,\n ...rest\n } = props\n const mergedInputProps = maxCharacterCount\n ? { ...inputPropsProp, maxLength: maxCharacterCount }\n : inputPropsProp\n const currentLength = typeof rest.value === 'string' ? rest.value.length : 0\n const Wrapper = useMemo(\n () =>\n noWrapInFormControl\n ? (props: PropsWithChildren<object>) => <>{props.children}</>\n : (props: PropsWithChildren<object>) => (\n <StyledFormControl fullWidth sx={{ my: 1 }}>\n {props.children}\n </StyledFormControl>\n ),\n [noWrapInFormControl],\n )\n return (\n <Wrapper>\n {(props.label || helperText) && (\n <Box className={styles.labelRow}>\n {props.label && (\n <InputLabel\n htmlFor={props.id || id}\n className={styles.inputLabel}\n sx={{\n '&::after': ({ palette }) => ({\n content: props.required ? '\"*\"' : undefined,\n marginLeft: '3px',\n color: palette.secondary.main,\n }),\n }}\n >\n {props.label}\n </InputLabel>\n )}\n {helperText && (\n <Typography variant=\"body2\" className={styles.helperText}>\n {helperText}\n </Typography>\n )}\n </Box>\n )}\n <InputBase id={id} inputProps={mergedInputProps} {...rest}></InputBase>\n {maxCharacterCount !== undefined && (\n <Typography variant=\"body2\" className={styles.charCount}>\n {currentLength}/{maxCharacterCount}\n </Typography>\n )}\n </Wrapper>\n )\n}\n"],"mappings":";;;;;;AAsCA,SAAwB,EAAU,GAAuB;CACvD,IAAM,IAAK,GAAO,EACZ,EACJ,wBACA,OAAO,GACP,eACA,sBACA,YAAY,GACZ,GAAG,MACD,GACE,IAAmB,IACrB;EAAE,GAAG;EAAgB,WAAW;EAAmB,GACnD,GACE,IAAgB,OAAO,EAAK,SAAU,WAAW,EAAK,MAAM,SAAS;AAY3E,QACE,kBAZc,QAEZ,KACK,MAAqC,kBAAA,GAAA,EAAA,UAAG,EAAM,UAAY,CAAA,IAC1D,MACC,kBAAC,GAAD;EAAmB,WAAA;EAAU,IAAI,EAAE,IAAI,GAAG;YACvC,EAAM;EACW,CAAA,EAE5B,CAAC,EAAoB,CAGpB,EAAD,EAAA,UAAA;GACI,EAAM,SAAS,MACf,kBAAC,GAAD;GAAK,WAAW,EAAO;aAAvB,CACG,EAAM,SACL,kBAAC,GAAD;IACE,SAAS,EAAM,MAAM;IACrB,WAAW,EAAO;IAClB,IAAI,EACF,aAAa,EAAE,kBAAe;KAC5B,SAAS,EAAM,WAAW,UAAQ,KAAA;KAClC,YAAY;KACZ,OAAO,EAAQ,UAAU;KAC1B,GACF;cAEA,EAAM;IACI,CAAA,EAEd,KACC,kBAAC,GAAD;IAAY,SAAQ;IAAQ,WAAW,EAAO;cAC3C;IACU,CAAA,CAEX;;EAER,kBAAC,GAAD;GAAe;GAAI,YAAY;GAAkB,GAAI;GAAkB,CAAA;EACtE,MAAsB,KAAA,KACrB,kBAAC,GAAD;GAAY,SAAQ;GAAQ,WAAW,EAAO;aAA9C;IACG;IAAc;IAAE;IACN;;EAEP,EAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimelinePhase.js","names":[],"sources":["../../../src/components/TimelinePlot/TimelinePhase.tsx"],"sourcesContent":["import React from 'react'\nimport { OBSERVATION_CARD } from '@/utils/SynapseConstants'\nimport { Dialog, DialogContent } from '@mui/material'\nimport {\n ColumnSingleValueFilterOperator,\n Row,\n} from '@sage-bionetworks/synapse-types'\nimport dayjs, { ManipulateType } from 'dayjs'\nimport Plotly, { Layout, PlotData } from 'plotly.js-basic-dist'\nimport { useMemo, useRef, useState } from 'react'\nimport CardContainerLogic from '../CardContainerLogic'\nimport Plot from '../Plot/Plot'\nimport { ObservationCardSchema } from '../row_renderers/ObservationCard'\n\ntype TimepointData = {\n timepoints: dayjs.Dayjs[]\n hoverOverIndex: number\n}\n\nconst getTimepointData = (\n start: dayjs.Dayjs,\n rowData: Row[],\n schema: ObservationCardSchema,\n hoverEventRowId?: number, //if supplied, will return the index of this row\n): TimepointData => {\n let hoverOverIndex = -1\n const timepoints = rowData.map((row, index) => {\n const time = parseFloat(row.values[schema.observationTime]!)\n const timeUnit = row.values[schema.observationTimeUnits]\n if (row.rowId == hoverEventRowId) {\n hoverOverIndex = index\n }\n return start.add(time, timeUnit as ManipulateType)\n })\n return {\n timepoints,\n hoverOverIndex,\n }\n}\n\nexport const getMaxDate = (timepoints: dayjs.Dayjs[]) => {\n if (timepoints.length == 0) {\n return dayjs().add(1, 'days')\n }\n return timepoints.reduce(\n (maxDateItem: dayjs.Dayjs, currentDateItem: dayjs.Dayjs) => {\n if (!maxDateItem || currentDateItem.isAfter(maxDateItem)) {\n return currentDateItem\n }\n return maxDateItem\n },\n )\n}\n\nconst getTimelineData = (timepointData: TimepointData, rowData: Row[]) => {\n // If this phase has no data, return an empty data line\n if (\n timepointData.timepoints == undefined ||\n timepointData.timepoints.length == 0\n ) {\n return [\n {\n x: [dayjs().format()],\n y: [0.5],\n mode: 'lines',\n line: {\n color: 'blue',\n width: 2,\n },\n customdata: [],\n hoverinfo: 'none',\n },\n ]\n }\n\n // first combine all of the Rows that share the same timepoint\n const combinedData: Record<string, Row[]> = timepointData.timepoints.reduce(\n (result, currentTimepoint, index) => {\n const resultRecord = result as Record<string, Row[]>\n if (!resultRecord[currentTimepoint.format()]) {\n resultRecord[currentTimepoint.format()] = []\n }\n resultRecord[currentTimepoint.format()].push(rowData[index])\n return result\n },\n {},\n )\n\n const data = timepointData.timepoints.map((timepoint, index) => {\n const utcFormattedTimepoint = timepoint.format()\n const isHoveredOver = index == timepointData.hoverOverIndex\n const rows = combinedData[utcFormattedTimepoint]\n const rowIds = rows.map(row => {\n return row.rowId\n })\n return {\n x: [utcFormattedTimepoint, utcFormattedTimepoint, utcFormattedTimepoint],\n y: [0, 0.5, 1],\n mode: 'lines',\n line: {\n color: isHoveredOver ? 'black' : 'gray',\n width: isHoveredOver ? 2 : 1,\n },\n // Add event into in the customdata\n customdata: [rowIds, rowIds, rowIds],\n // but tell Plotly that we do not want it to show a hover tooltip (we're going to handle this)\n hoverinfo: 'none',\n }\n })\n return data\n}\n\nconst getLayout = (\n start: dayjs.Dayjs,\n end: dayjs.Dayjs,\n color: string,\n timelineData: Plotly.Data[],\n rowData: Row[],\n schema: ObservationCardSchema,\n): Partial<Layout> => {\n const xTickVals = timelineData.map(value => {\n // return the utcFormattedTimepoint\n return (value as PlotData).x[0]\n })\n const xTickText = timelineData.map(value => {\n const rowIds = (value as PlotData).customdata[0] as number[] | undefined\n if (rowIds && rowIds.length > 0) {\n const count = rowIds.length\n const rows = rowData?.filter(row => {\n return rowIds[0] == row.rowId\n })\n if (rows && rows.length > 0) {\n const row = rows[0]\n const time = parseFloat(row.values[schema.observationTime]!)\n const timeUnits = row.values[\n schema.observationTimeUnits\n ] as ManipulateType\n const countString = count > 1 ? `(${count})` : ''\n return `${time} ${timeUnits} ${countString}`\n }\n }\n return ''\n })\n\n return {\n hovermode: 'closest',\n dragmode: false, //disallow interaction\n showlegend: false,\n xaxis: {\n showgrid: false,\n showticklabels: true,\n showline: false,\n zeroline: false,\n tickvals: xTickVals,\n ticktext: xTickText,\n tickangle: -45,\n },\n\n yaxis: {\n range: [0, 1.25],\n showgrid: false,\n zeroline: false,\n showline: false,\n showticklabels: false,\n },\n // Each phase has a shape\n shapes: [\n {\n type: 'rect',\n x0: start.subtract(end.diff(start) / 10).format(), // add 10% to the left side of the graph\n x1: end.add(end.diff(start) / 3).format(), // add 33% to right side of the graph\n y0: 0.25,\n y1: 0.75,\n fillcolor: color,\n opacity: 0.8,\n line: {\n width: 0,\n },\n },\n ],\n margin: {\n l: 0,\n r: 0,\n t: 60,\n b: 60,\n },\n // autosize: false,\n }\n}\n\ntype TimelinePhaseProps = {\n name: string\n color: string\n rowData: Row[]\n schema: ObservationCardSchema\n widthPx: number\n sql: string\n}\n\nconst TimelinePhase = ({\n color,\n rowData,\n schema,\n widthPx,\n sql,\n}: TimelinePhaseProps): React.ReactNode => {\n const [clickEvent, setClickEvent] = useState<Plotly.PlotMouseEvent>()\n const [hoverEvent, setHoverEvent] = useState<Plotly.PlotHoverEvent>()\n const [plotKey, setPlotKey] = useState(1)\n const start = dayjs()\n // hide the hover UI if we detect that the user moves the mouse outside of this component boundary\n const componentRef = useRef<HTMLDivElement>(null)\n const rowIds = clickEvent?.points[0].customdata as unknown as (\n | number\n | undefined\n )[]\n const selectedRows = rowData?.filter(row => {\n return rowIds?.includes(row.rowId)\n })\n\n const hoverEventRowIds = hoverEvent?.points[0].customdata as unknown as (\n | number\n | undefined\n )[]\n\n const timepointData = useMemo(() => {\n return getTimepointData(\n start,\n rowData,\n schema,\n hoverEventRowIds?.length > 0 ? hoverEventRowIds[0] : undefined,\n )\n }, [hoverEventRowIds, rowData, schema, start])\n\n const end = getMaxDate(timepointData.timepoints)\n\n const timelineData = useMemo(() => {\n return getTimelineData(timepointData, rowData)\n }, [timepointData, rowData])\n\n const selectedRowIds = selectedRows.map(row => row.rowId!)\n\n return (\n <div ref={componentRef} style={{ width: widthPx }}>\n <Plot\n key={`Plot-${color}-${plotKey}`}\n style={{ width: widthPx, height: '220px' }}\n data={timelineData}\n layout={getLayout(start, end, color, timelineData, rowData, schema)}\n config={{ displayModeBar: false }}\n useResizeHandler={true}\n onClick={eventData => {\n setClickEvent(eventData)\n setHoverEvent(undefined)\n }}\n // PORTALS-2861: To avoid the plot handling the double-click, change the key so the Plotly graph (gd) associated\n // to the click event is invalid.\n onDoubleClick={() => {\n setPlotKey(plotKey + 1)\n }}\n onHover={eventData => {\n setHoverEvent(eventData)\n }}\n onUnhover={() => {\n setHoverEvent(undefined)\n }}\n />\n <Dialog\n onClose={() => setClickEvent(undefined)}\n open={selectedRows && selectedRows.length > 0}\n >\n <DialogContent>\n <CardContainerLogic\n topLevelEnumeratedFacetToFilter={{ columnName: 'observationType' }}\n sql={sql}\n searchParams={{ ['ROW_ID']: selectedRowIds.map(String).join(',') }}\n sqlOperator={ColumnSingleValueFilterOperator.IN}\n lockedColumn={{ columnName: 'ROW_ID' }}\n cardConfiguration={{ type: OBSERVATION_CARD }}\n initialLimit={3}\n />\n </DialogContent>\n </Dialog>\n </div>\n )\n}\n\nexport default TimelinePhase\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TimelinePhase.js","names":[],"sources":["../../../src/components/TimelinePlot/TimelinePhase.tsx"],"sourcesContent":["import React from 'react'\nimport { OBSERVATION_CARD } from '@/utils/SynapseConstants'\nimport { Dialog, DialogContent } from '@mui/material'\nimport {\n ColumnSingleValueFilterOperator,\n Row,\n} from '@sage-bionetworks/synapse-types'\nimport dayjs, { ManipulateType } from 'dayjs'\nimport Plotly, { Layout, PlotData } from 'plotly.js-basic-dist'\nimport { useMemo, useRef, useState } from 'react'\nimport CardContainerLogic from '../CardContainerLogic'\nimport Plot from '../Plot/Plot'\nimport { ObservationCardSchema } from '../row_renderers/ObservationCard'\n\ntype TimepointData = {\n timepoints: dayjs.Dayjs[]\n hoverOverIndex: number\n}\n\nconst getTimepointData = (\n start: dayjs.Dayjs,\n rowData: Row[],\n schema: ObservationCardSchema,\n hoverEventRowId?: number, //if supplied, will return the index of this row\n): TimepointData => {\n let hoverOverIndex = -1\n const timepoints = rowData.map((row, index) => {\n const time = parseFloat(row.values[schema.observationTime]!)\n const timeUnit = row.values[schema.observationTimeUnits]\n if (row.rowId == hoverEventRowId) {\n hoverOverIndex = index\n }\n return start.add(time, timeUnit as ManipulateType)\n })\n return {\n timepoints,\n hoverOverIndex,\n }\n}\n\nexport const getMaxDate = (timepoints: dayjs.Dayjs[]) => {\n if (timepoints.length == 0) {\n return dayjs().add(1, 'days')\n }\n return timepoints.reduce(\n (maxDateItem: dayjs.Dayjs, currentDateItem: dayjs.Dayjs) => {\n if (!maxDateItem || currentDateItem.isAfter(maxDateItem)) {\n return currentDateItem\n }\n return maxDateItem\n },\n )\n}\n\nconst getTimelineData = (timepointData: TimepointData, rowData: Row[]) => {\n // If this phase has no data, return an empty data line\n if (\n timepointData.timepoints == undefined ||\n timepointData.timepoints.length == 0\n ) {\n return [\n {\n x: [dayjs().format()],\n y: [0.5],\n mode: 'lines',\n line: {\n color: 'blue',\n width: 2,\n },\n customdata: [],\n hoverinfo: 'none',\n },\n ]\n }\n\n // first combine all of the Rows that share the same timepoint\n const combinedData: Record<string, Row[]> = timepointData.timepoints.reduce(\n (result, currentTimepoint, index) => {\n const resultRecord = result as Record<string, Row[]>\n if (!resultRecord[currentTimepoint.format()]) {\n resultRecord[currentTimepoint.format()] = []\n }\n resultRecord[currentTimepoint.format()].push(rowData[index])\n return result\n },\n {},\n )\n\n const data = timepointData.timepoints.map((timepoint, index) => {\n const utcFormattedTimepoint = timepoint.format()\n const isHoveredOver = index == timepointData.hoverOverIndex\n const rows = combinedData[utcFormattedTimepoint]\n const rowIds = rows.map(row => {\n return row.rowId\n })\n return {\n x: [utcFormattedTimepoint, utcFormattedTimepoint, utcFormattedTimepoint],\n y: [0, 0.5, 1],\n mode: 'lines',\n line: {\n color: isHoveredOver ? 'black' : 'gray',\n width: isHoveredOver ? 2 : 1,\n },\n // Add event into in the customdata\n customdata: [rowIds, rowIds, rowIds],\n // but tell Plotly that we do not want it to show a hover tooltip (we're going to handle this)\n hoverinfo: 'none',\n }\n })\n return data\n}\n\nconst getLayout = (\n start: dayjs.Dayjs,\n end: dayjs.Dayjs,\n color: string,\n timelineData: Plotly.Data[],\n rowData: Row[],\n schema: ObservationCardSchema,\n): Partial<Layout> => {\n const xTickVals = timelineData.map(value => {\n // return the utcFormattedTimepoint\n return (value as PlotData).x[0]\n })\n const xTickText = timelineData.map(value => {\n const rowIds = (value as PlotData).customdata[0] as number[] | undefined\n if (rowIds && rowIds.length > 0) {\n const count = rowIds.length\n const rows = rowData?.filter(row => {\n return rowIds[0] == row.rowId\n })\n if (rows && rows.length > 0) {\n const row = rows[0]\n const time = parseFloat(row.values[schema.observationTime]!)\n const timeUnits = row.values[\n schema.observationTimeUnits\n ] as ManipulateType\n const countString = count > 1 ? `(${count})` : ''\n return `${time} ${timeUnits} ${countString}`\n }\n }\n return ''\n })\n\n return {\n hovermode: 'closest',\n dragmode: false, //disallow interaction\n showlegend: false,\n xaxis: {\n showgrid: false,\n showticklabels: true,\n showline: false,\n zeroline: false,\n tickvals: xTickVals,\n ticktext: xTickText,\n tickangle: -45,\n },\n\n yaxis: {\n range: [0, 1.25],\n showgrid: false,\n zeroline: false,\n showline: false,\n showticklabels: false,\n },\n // Each phase has a shape\n shapes: [\n {\n type: 'rect',\n x0: start.subtract(end.diff(start) / 10).format(), // add 10% to the left side of the graph\n x1: end.add(end.diff(start) / 3).format(), // add 33% to right side of the graph\n y0: 0.25,\n y1: 0.75,\n fillcolor: color,\n opacity: 0.8,\n line: {\n width: 0,\n },\n },\n ],\n margin: {\n l: 0,\n r: 0,\n t: 60,\n b: 60,\n },\n // autosize: false,\n }\n}\n\ntype TimelinePhaseProps = {\n name: string\n color: string\n rowData: Row[]\n schema: ObservationCardSchema\n widthPx: number\n sql: string\n}\n\nconst TimelinePhase = ({\n color,\n rowData,\n schema,\n widthPx,\n sql,\n}: TimelinePhaseProps): React.ReactNode => {\n const [clickEvent, setClickEvent] = useState<Plotly.PlotMouseEvent>()\n const [hoverEvent, setHoverEvent] = useState<Plotly.PlotHoverEvent>()\n const [plotKey, setPlotKey] = useState(1)\n const start = dayjs()\n // hide the hover UI if we detect that the user moves the mouse outside of this component boundary\n const componentRef = useRef<HTMLDivElement>(null)\n const rowIds = clickEvent?.points[0].customdata as unknown as (\n | number\n | undefined\n )[]\n const selectedRows = rowData?.filter(row => {\n return rowIds?.includes(row.rowId)\n })\n\n const hoverEventRowIds = hoverEvent?.points[0].customdata as unknown as (\n | number\n | undefined\n )[]\n\n const timepointData = useMemo(() => {\n return getTimepointData(\n start,\n rowData,\n schema,\n hoverEventRowIds?.length > 0 ? hoverEventRowIds[0] : undefined,\n )\n }, [hoverEventRowIds, rowData, schema, start])\n\n const end = getMaxDate(timepointData.timepoints)\n\n const timelineData = useMemo(() => {\n return getTimelineData(timepointData, rowData)\n }, [timepointData, rowData])\n\n const selectedRowIds = selectedRows.map(row => row.rowId!)\n\n return (\n <div ref={componentRef} style={{ width: widthPx }}>\n <Plot\n key={`Plot-${color}-${plotKey}`}\n style={{ width: widthPx, height: '220px' }}\n data={timelineData}\n layout={getLayout(start, end, color, timelineData, rowData, schema)}\n config={{ displayModeBar: false }}\n useResizeHandler={true}\n onClick={eventData => {\n setClickEvent(eventData)\n setHoverEvent(undefined)\n }}\n // PORTALS-2861: To avoid the plot handling the double-click, change the key so the Plotly graph (gd) associated\n // to the click event is invalid.\n onDoubleClick={() => {\n setPlotKey(plotKey + 1)\n }}\n onHover={eventData => {\n setHoverEvent(eventData)\n }}\n onUnhover={() => {\n setHoverEvent(undefined)\n }}\n />\n <Dialog\n onClose={() => setClickEvent(undefined)}\n open={selectedRows && selectedRows.length > 0}\n >\n <DialogContent>\n <CardContainerLogic\n topLevelEnumeratedFacetToFilter={{ columnName: 'observationType' }}\n sql={sql}\n searchParams={{ ['ROW_ID']: selectedRowIds.map(String).join(',') }}\n sqlOperator={ColumnSingleValueFilterOperator.IN}\n lockedColumn={{ columnName: 'ROW_ID' }}\n cardConfiguration={{ type: OBSERVATION_CARD }}\n initialLimit={3}\n />\n </DialogContent>\n </Dialog>\n </div>\n )\n}\n\nexport default TimelinePhase\n"],"mappings":";;;;;;;;;;AAmBA,IAAM,KACJ,GACA,GACA,GACA,MACkB;CAClB,IAAI,IAAiB;AASrB,QAAO;EACL,YATiB,EAAQ,KAAK,GAAK,MAAU;GAC7C,IAAM,IAAO,WAAW,EAAI,OAAO,EAAO,iBAAkB,EACtD,IAAW,EAAI,OAAO,EAAO;AAInC,UAHI,EAAI,SAAS,MACf,IAAiB,IAEZ,EAAM,IAAI,GAAM,EAA2B;IAGlD;EACA;EACD;GAGU,KAAc,MACrB,EAAW,UAAU,IAChB,GAAO,CAAC,IAAI,GAAG,OAAO,GAExB,EAAW,QACf,GAA0B,MACrB,CAAC,KAAe,EAAgB,QAAQ,EAAY,GAC/C,IAEF,EAEV,EAGG,KAAmB,GAA8B,MAAmB;AAExE,KACE,EAAc,cAAc,QAC5B,EAAc,WAAW,UAAU,EAEnC,QAAO,CACL;EACE,GAAG,CAAC,GAAO,CAAC,QAAQ,CAAC;EACrB,GAAG,CAAC,GAAI;EACR,MAAM;EACN,MAAM;GACJ,OAAO;GACP,OAAO;GACR;EACD,YAAY,EAAE;EACd,WAAW;EACZ,CACF;CAIH,IAAM,IAAsC,EAAc,WAAW,QAClE,GAAQ,GAAkB,MAAU;EACnC,IAAM,IAAe;AAKrB,SAJK,EAAa,EAAiB,QAAQ,MACzC,EAAa,EAAiB,QAAQ,IAAI,EAAE,GAE9C,EAAa,EAAiB,QAAQ,EAAE,KAAK,EAAQ,GAAO,EACrD;IAET,EAAE,CACH;AAuBD,QArBa,EAAc,WAAW,KAAK,GAAW,MAAU;EAC9D,IAAM,IAAwB,EAAU,QAAQ,EAC1C,IAAgB,KAAS,EAAc,gBAEvC,IADO,EAAa,GACN,KAAI,MACf,EAAI,MACX;AACF,SAAO;GACL,GAAG;IAAC;IAAuB;IAAuB;IAAsB;GACxE,GAAG;IAAC;IAAG;IAAK;IAAE;GACd,MAAM;GACN,MAAM;IACJ,OAAO,IAAgB,UAAU;IACjC,OAAO,IAAgB,IAAI;IAC5B;GAED,YAAY;IAAC;IAAQ;IAAQ;IAAO;GAEpC,WAAW;GACZ;GAEI;GAGH,KACJ,GACA,GACA,GACA,GACA,GACA,OA0BO;CACL,WAAW;CACX,UAAU;CACV,YAAY;CACZ,OAAO;EACL,UAAU;EACV,gBAAgB;EAChB,UAAU;EACV,UAAU;EACV,UAjCc,EAAa,KAAI,MAEzB,EAAmB,EAAE,GA+BjB;EACV,UA9Bc,EAAa,KAAI,MAAS;GAC1C,IAAM,IAAU,EAAmB,WAAW;AAC9C,OAAI,KAAU,EAAO,SAAS,GAAG;IAC/B,IAAM,IAAQ,EAAO,QACf,IAAO,GAAS,QAAO,MACpB,EAAO,MAAM,EAAI,MACxB;AACF,QAAI,KAAQ,EAAK,SAAS,GAAG;KAC3B,IAAM,IAAM,EAAK;AAMjB,YAAO,GALM,WAAW,EAAI,OAAO,EAAO,iBAKhC,CAAK,GAJG,EAAI,OACpB,EAAO,sBAGmB,GADR,IAAQ,IAAI,IAAI,EAAM,KAAK;;;AAInD,UAAO;IAaK;EACV,WAAW;EACZ;CAED,OAAO;EACL,OAAO,CAAC,GAAG,KAAK;EAChB,UAAU;EACV,UAAU;EACV,UAAU;EACV,gBAAgB;EACjB;CAED,QAAQ,CACN;EACE,MAAM;EACN,IAAI,EAAM,SAAS,EAAI,KAAK,EAAM,GAAG,GAAG,CAAC,QAAQ;EACjD,IAAI,EAAI,IAAI,EAAI,KAAK,EAAM,GAAG,EAAE,CAAC,QAAQ;EACzC,IAAI;EACJ,IAAI;EACJ,WAAW;EACX,SAAS;EACT,MAAM,EACJ,OAAO,GACR;EACF,CACF;CACD,QAAQ;EACN,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACJ;CAEF,GAYG,KAAiB,EACrB,UACA,YACA,WACA,YACA,aACyC;CACzC,IAAM,CAAC,GAAY,KAAiB,GAAiC,EAC/D,CAAC,GAAY,KAAiB,GAAiC,EAC/D,CAAC,GAAS,KAAc,EAAS,EAAE,EACnC,IAAQ,GAAO,EAEf,IAAe,EAAuB,KAAK,EAC3C,IAAS,GAAY,OAAO,GAAG,YAI/B,IAAe,GAAS,QAAO,MAC5B,GAAQ,SAAS,EAAI,MAAM,CAClC,EAEI,IAAmB,GAAY,OAAO,GAAG,YAKzC,IAAgB,QACb,EACL,GACA,GACA,GACA,GAAkB,SAAS,IAAI,EAAiB,KAAK,KAAA,EACtD,EACA;EAAC;EAAkB;EAAS;EAAQ;EAAM,CAAC,EAExC,IAAM,EAAW,EAAc,WAAW,EAE1C,IAAe,QACZ,EAAgB,GAAe,EAAQ,EAC7C,CAAC,GAAe,EAAQ,CAAC,EAEtB,IAAiB,EAAa,KAAI,MAAO,EAAI,MAAO;AAE1D,QACE,kBAAC,OAAD;EAAK,KAAK;EAAc,OAAO,EAAE,OAAO,GAAS;YAAjD,CACE,kBAAC,GAAD;GAEE,OAAO;IAAE,OAAO;IAAS,QAAQ;IAAS;GAC1C,MAAM;GACN,QAAQ,EAAU,GAAO,GAAK,GAAO,GAAc,GAAS,EAAO;GACnE,QAAQ,EAAE,gBAAgB,IAAO;GACjC,kBAAkB;GAClB,UAAS,MAAa;AAEpB,IADA,EAAc,EAAU,EACxB,EAAc,KAAA,EAAU;;GAI1B,qBAAqB;AACnB,MAAW,IAAU,EAAE;;GAEzB,UAAS,MAAa;AACpB,MAAc,EAAU;;GAE1B,iBAAiB;AACf,MAAc,KAAA,EAAU;;GAE1B,EArBK,QAAQ,EAAM,GAAG,IAqBtB,EACF,kBAAC,GAAD;GACE,eAAe,EAAc,KAAA,EAAU;GACvC,MAAM,KAAgB,EAAa,SAAS;aAE5C,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IACE,iCAAiC,EAAE,YAAY,mBAAmB;IAC7D;IACL,cAAc,EAAG,QAAW,EAAe,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE;IAClE,aAAa,EAAgC;IAC7C,cAAc,EAAE,YAAY,UAAU;IACtC,mBAAmB,EAAE,MAAM,GAAkB;IAC7C,cAAc;IACd,CAAA,EACY,CAAA;GACT,CAAA,CACL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimelinePlot.js","names":[],"sources":["../../../src/components/TimelinePlot/TimelinePlot.tsx"],"sourcesContent":["import React from 'react'\nimport { useGetFullTableQueryResults } from '@/synapse-queries'\nimport {\n getAdditionalFilters,\n parseEntityIdFromSqlStatement,\n SQLOperator,\n} from '@/utils/functions'\nimport { getHeaderIndex } from '@/utils/functions/queryUtils'\nimport { BUNDLE_MASK_QUERY_RESULTS } from '@/utils/SynapseConstants'\nimport { Skeleton, Typography } from '@mui/material'\nimport { Box } from '@mui/system'\nimport { useMeasure } from '@react-hookz/web'\nimport {\n ColumnMultiValueFunction,\n ColumnMultiValueFunctionQueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport { useMemo, useState } from 'react'\nimport { getColorPalette } from '../ColorGradient/ColorGradient'\nimport { ObservationCardSchema } from '../row_renderers/ObservationCard'\nimport hardcodedPhasesQueryResponseData, {\n phaseObservationIndex,\n phaseSpeciesIndex,\n} from './phasesQueryResponseData'\nimport TimelineLegendItem from './TimelineLegendItem'\nimport TimelinePhase from './TimelinePhase'\nimport TimelinePlotSpeciesSelector from './TimelinePlotSpeciesSelector'\n\nconst OBSERVATION_PHASE_COLUMN_NAME = 'observationphase'\nconst OBSERVATION_TIME_COLUMN_NAME = 'observationtime'\nconst OBSERVATION_TIME_UNITS_COLUMN_NAME = 'observationtimeunits'\nconst OBSERVATION_SUBMITTER_NAME_COLUMN_NAME = 'observationsubmittername'\nconst OBSERVATION_TEXT_COLUMN_NAME = 'observationtext'\nconst OBSERVATION_TYPE_COLUMN_NAME = 'observationtype'\nconst OBSERVATION_SUBMITTER_USER_ID_COLUMN_NAME = 'synapseid'\nconst OBSERVATION_DOI_COLUMN_NAME = 'doi'\n\nexport type TimelinePlotProps = {\n sql: string\n searchParams?: Record<string, string>\n sqlOperator?: SQLOperator\n title?: string\n subTitle?: string\n defaultSpecies?: string //for test\n}\nexport const TimelinePlot = ({\n sql,\n searchParams,\n sqlOperator,\n defaultSpecies,\n title,\n subTitle,\n}: TimelinePlotProps): React.ReactNode => {\n // Fetch the table data\n const eventsTableId = parseEntityIdFromSqlStatement(sql)\n const [species, setSpecies] = useState<string | undefined | null>(\n defaultSpecies,\n )\n const [plotContainerMeasurements, plotContainerRef] =\n useMeasure<HTMLDivElement>()\n const queryFilters = getAdditionalFilters(searchParams, sqlOperator) ?? []\n const speciesFilter: ColumnMultiValueFunctionQueryFilter | undefined = species\n ? {\n columnName: 'species',\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnMultiValueFunctionQueryFilter',\n values: [species],\n function: ColumnMultiValueFunction.HAS,\n _function: ColumnMultiValueFunction.HAS,\n }\n : undefined\n const additionalFilters = [...queryFilters]\n if (speciesFilter) {\n additionalFilters.push(speciesFilter)\n }\n const eventTableQuery = useGetFullTableQueryResults(\n {\n entityId: eventsTableId,\n query: {\n sql: `${sql} WHERE observationTime IS NOT NULL`,\n sort: [\n {\n column: 'observationTime',\n direction: 'ASC',\n },\n ],\n additionalFilters: additionalFilters,\n },\n\n partMask: BUNDLE_MASK_QUERY_RESULTS,\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n },\n {\n enabled: !!species,\n },\n )\n\n const { data: eventsData, isLoading } = eventTableQuery\n\n // filter the phases query response data to the specific species\n const phaseData = useMemo(() => {\n if (species) {\n const phasesForTargetSpecies =\n hardcodedPhasesQueryResponseData.queryResult?.queryResults.rows.filter(\n row => {\n return row.values[phaseSpeciesIndex] == species\n },\n )\n return phasesForTargetSpecies\n } else return undefined\n }, [species])\n\n if (isLoading) {\n return <LoadingTimelinePlot />\n }\n const observationPhaseIndex = getHeaderIndex(\n OBSERVATION_PHASE_COLUMN_NAME,\n eventsData,\n )\n const observationTimeIndex = getHeaderIndex(\n OBSERVATION_TIME_COLUMN_NAME,\n eventsData,\n )\n const observationTimeUnitIndex = getHeaderIndex(\n OBSERVATION_TIME_UNITS_COLUMN_NAME,\n eventsData,\n )\n const observationSubmitterNameIndex = getHeaderIndex(\n OBSERVATION_SUBMITTER_NAME_COLUMN_NAME,\n eventsData,\n )\n const observationTextIndex = getHeaderIndex(\n OBSERVATION_TEXT_COLUMN_NAME,\n eventsData,\n )\n const observationDoiIndex = getHeaderIndex(\n OBSERVATION_DOI_COLUMN_NAME,\n eventsData,\n )\n\n const observationTypeIndex = getHeaderIndex(\n OBSERVATION_TYPE_COLUMN_NAME,\n eventsData,\n )\n const submitterUserIdIndex = getHeaderIndex(\n OBSERVATION_SUBMITTER_USER_ID_COLUMN_NAME,\n eventsData,\n )\n\n const schema: ObservationCardSchema = {\n observationSubmitterName: observationSubmitterNameIndex,\n synapseId: submitterUserIdIndex,\n observationType: observationTypeIndex,\n observationText: observationTextIndex,\n observationTime: observationTimeIndex,\n observationTimeUnits: observationTimeUnitIndex,\n doi: observationDoiIndex,\n }\n\n const widthPx =\n plotContainerMeasurements?.width && phaseData\n ? plotContainerMeasurements?.width / phaseData.length\n : 0\n const gridTemplateColumns = phaseData?.map(() => 'auto').join(' ')\n\n return (\n <>\n {species && (\n <>\n {title && <Typography variant=\"h2\">{title}</Typography>}\n {subTitle && (\n <Typography variant=\"body1Italic\" sx={{ margin: '10px 0px' }}>\n {subTitle}\n </Typography>\n )}\n </>\n )}\n <Box>\n <Box sx={{ display: 'flex', justifyContent: 'space-between' }}>\n {/* Species selector */}\n <Box>\n {!defaultSpecies && (\n <TimelinePlotSpeciesSelector\n setSpecies={setSpecies}\n species={species}\n sql={sql}\n additionalFilters={queryFilters}\n />\n )}\n </Box>\n {/* Legend */}\n {phaseData && (\n <Box\n sx={{ display: 'flex', justifyContent: 'flex-end', gap: '25px' }}\n >\n {phaseData.map((phaseRow, index) => {\n const { colorPalette } = getColorPalette(index, 1)\n return (\n <TimelineLegendItem\n key={phaseRow.rowId}\n color={colorPalette[0]}\n phaseName={phaseRow.values[phaseObservationIndex]}\n />\n )\n })}\n </Box>\n )}\n </Box>\n {/* Phase plots */}\n {species && phaseData && (\n <div ref={plotContainerRef}>\n <Box\n sx={{\n display: 'inline-grid',\n gridTemplateColumns,\n minWidth: plotContainerMeasurements?.width,\n maxWidth: plotContainerMeasurements?.width,\n }}\n className=\"forcePlotlyDefaultCursor\"\n >\n {phaseData.map((phaseRow, index) => {\n const { colorPalette } = getColorPalette(index, 1)\n const phaseEventRows =\n eventsData?.queryResult?.queryResults.rows.filter(row => {\n return (\n row.values[observationPhaseIndex] ==\n phaseRow.values[phaseObservationIndex]\n )\n })\n\n return phaseEventRows ? (\n <TimelinePhase\n key={phaseRow.rowId}\n name={phaseRow.values[phaseObservationIndex]!}\n color={colorPalette[0]}\n rowData={phaseEventRows}\n schema={schema}\n widthPx={widthPx}\n sql={sql}\n />\n ) : (\n <></>\n )\n })}\n </Box>\n </div>\n )}\n </Box>\n </>\n )\n}\n\nexport const LoadingTimelinePlot = (): React.ReactNode => {\n return (\n <Box>\n <Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: '10px' }}>\n <Skeleton height=\"45px\" width=\"80px\" />\n <Skeleton height=\"45px\" width=\"80px\" />\n </Box>\n <Box sx={{ display: 'flex' }}>\n <Skeleton height=\"150px\" width=\"100%\" />\n </Box>\n </Box>\n )\n}\n\nexport default TimelinePlot\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TimelinePlot.js","names":[],"sources":["../../../src/components/TimelinePlot/TimelinePlot.tsx"],"sourcesContent":["import React from 'react'\nimport { useGetFullTableQueryResults } from '@/synapse-queries'\nimport {\n getAdditionalFilters,\n parseEntityIdFromSqlStatement,\n SQLOperator,\n} from '@/utils/functions'\nimport { getHeaderIndex } from '@/utils/functions/queryUtils'\nimport { BUNDLE_MASK_QUERY_RESULTS } from '@/utils/SynapseConstants'\nimport { Skeleton, Typography } from '@mui/material'\nimport { Box } from '@mui/system'\nimport { useMeasure } from '@react-hookz/web'\nimport {\n ColumnMultiValueFunction,\n ColumnMultiValueFunctionQueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport { useMemo, useState } from 'react'\nimport { getColorPalette } from '../ColorGradient/ColorGradient'\nimport { ObservationCardSchema } from '../row_renderers/ObservationCard'\nimport hardcodedPhasesQueryResponseData, {\n phaseObservationIndex,\n phaseSpeciesIndex,\n} from './phasesQueryResponseData'\nimport TimelineLegendItem from './TimelineLegendItem'\nimport TimelinePhase from './TimelinePhase'\nimport TimelinePlotSpeciesSelector from './TimelinePlotSpeciesSelector'\n\nconst OBSERVATION_PHASE_COLUMN_NAME = 'observationphase'\nconst OBSERVATION_TIME_COLUMN_NAME = 'observationtime'\nconst OBSERVATION_TIME_UNITS_COLUMN_NAME = 'observationtimeunits'\nconst OBSERVATION_SUBMITTER_NAME_COLUMN_NAME = 'observationsubmittername'\nconst OBSERVATION_TEXT_COLUMN_NAME = 'observationtext'\nconst OBSERVATION_TYPE_COLUMN_NAME = 'observationtype'\nconst OBSERVATION_SUBMITTER_USER_ID_COLUMN_NAME = 'synapseid'\nconst OBSERVATION_DOI_COLUMN_NAME = 'doi'\n\nexport type TimelinePlotProps = {\n sql: string\n searchParams?: Record<string, string>\n sqlOperator?: SQLOperator\n title?: string\n subTitle?: string\n defaultSpecies?: string //for test\n}\nexport const TimelinePlot = ({\n sql,\n searchParams,\n sqlOperator,\n defaultSpecies,\n title,\n subTitle,\n}: TimelinePlotProps): React.ReactNode => {\n // Fetch the table data\n const eventsTableId = parseEntityIdFromSqlStatement(sql)\n const [species, setSpecies] = useState<string | undefined | null>(\n defaultSpecies,\n )\n const [plotContainerMeasurements, plotContainerRef] =\n useMeasure<HTMLDivElement>()\n const queryFilters = getAdditionalFilters(searchParams, sqlOperator) ?? []\n const speciesFilter: ColumnMultiValueFunctionQueryFilter | undefined = species\n ? {\n columnName: 'species',\n concreteType:\n 'org.sagebionetworks.repo.model.table.ColumnMultiValueFunctionQueryFilter',\n values: [species],\n function: ColumnMultiValueFunction.HAS,\n _function: ColumnMultiValueFunction.HAS,\n }\n : undefined\n const additionalFilters = [...queryFilters]\n if (speciesFilter) {\n additionalFilters.push(speciesFilter)\n }\n const eventTableQuery = useGetFullTableQueryResults(\n {\n entityId: eventsTableId,\n query: {\n sql: `${sql} WHERE observationTime IS NOT NULL`,\n sort: [\n {\n column: 'observationTime',\n direction: 'ASC',\n },\n ],\n additionalFilters: additionalFilters,\n },\n\n partMask: BUNDLE_MASK_QUERY_RESULTS,\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n },\n {\n enabled: !!species,\n },\n )\n\n const { data: eventsData, isLoading } = eventTableQuery\n\n // filter the phases query response data to the specific species\n const phaseData = useMemo(() => {\n if (species) {\n const phasesForTargetSpecies =\n hardcodedPhasesQueryResponseData.queryResult?.queryResults.rows.filter(\n row => {\n return row.values[phaseSpeciesIndex] == species\n },\n )\n return phasesForTargetSpecies\n } else return undefined\n }, [species])\n\n if (isLoading) {\n return <LoadingTimelinePlot />\n }\n const observationPhaseIndex = getHeaderIndex(\n OBSERVATION_PHASE_COLUMN_NAME,\n eventsData,\n )\n const observationTimeIndex = getHeaderIndex(\n OBSERVATION_TIME_COLUMN_NAME,\n eventsData,\n )\n const observationTimeUnitIndex = getHeaderIndex(\n OBSERVATION_TIME_UNITS_COLUMN_NAME,\n eventsData,\n )\n const observationSubmitterNameIndex = getHeaderIndex(\n OBSERVATION_SUBMITTER_NAME_COLUMN_NAME,\n eventsData,\n )\n const observationTextIndex = getHeaderIndex(\n OBSERVATION_TEXT_COLUMN_NAME,\n eventsData,\n )\n const observationDoiIndex = getHeaderIndex(\n OBSERVATION_DOI_COLUMN_NAME,\n eventsData,\n )\n\n const observationTypeIndex = getHeaderIndex(\n OBSERVATION_TYPE_COLUMN_NAME,\n eventsData,\n )\n const submitterUserIdIndex = getHeaderIndex(\n OBSERVATION_SUBMITTER_USER_ID_COLUMN_NAME,\n eventsData,\n )\n\n const schema: ObservationCardSchema = {\n observationSubmitterName: observationSubmitterNameIndex,\n synapseId: submitterUserIdIndex,\n observationType: observationTypeIndex,\n observationText: observationTextIndex,\n observationTime: observationTimeIndex,\n observationTimeUnits: observationTimeUnitIndex,\n doi: observationDoiIndex,\n }\n\n const widthPx =\n plotContainerMeasurements?.width && phaseData\n ? plotContainerMeasurements?.width / phaseData.length\n : 0\n const gridTemplateColumns = phaseData?.map(() => 'auto').join(' ')\n\n return (\n <>\n {species && (\n <>\n {title && <Typography variant=\"h2\">{title}</Typography>}\n {subTitle && (\n <Typography variant=\"body1Italic\" sx={{ margin: '10px 0px' }}>\n {subTitle}\n </Typography>\n )}\n </>\n )}\n <Box>\n <Box sx={{ display: 'flex', justifyContent: 'space-between' }}>\n {/* Species selector */}\n <Box>\n {!defaultSpecies && (\n <TimelinePlotSpeciesSelector\n setSpecies={setSpecies}\n species={species}\n sql={sql}\n additionalFilters={queryFilters}\n />\n )}\n </Box>\n {/* Legend */}\n {phaseData && (\n <Box\n sx={{ display: 'flex', justifyContent: 'flex-end', gap: '25px' }}\n >\n {phaseData.map((phaseRow, index) => {\n const { colorPalette } = getColorPalette(index, 1)\n return (\n <TimelineLegendItem\n key={phaseRow.rowId}\n color={colorPalette[0]}\n phaseName={phaseRow.values[phaseObservationIndex]}\n />\n )\n })}\n </Box>\n )}\n </Box>\n {/* Phase plots */}\n {species && phaseData && (\n <div ref={plotContainerRef}>\n <Box\n sx={{\n display: 'inline-grid',\n gridTemplateColumns,\n minWidth: plotContainerMeasurements?.width,\n maxWidth: plotContainerMeasurements?.width,\n }}\n className=\"forcePlotlyDefaultCursor\"\n >\n {phaseData.map((phaseRow, index) => {\n const { colorPalette } = getColorPalette(index, 1)\n const phaseEventRows =\n eventsData?.queryResult?.queryResults.rows.filter(row => {\n return (\n row.values[observationPhaseIndex] ==\n phaseRow.values[phaseObservationIndex]\n )\n })\n\n return phaseEventRows ? (\n <TimelinePhase\n key={phaseRow.rowId}\n name={phaseRow.values[phaseObservationIndex]!}\n color={colorPalette[0]}\n rowData={phaseEventRows}\n schema={schema}\n widthPx={widthPx}\n sql={sql}\n />\n ) : (\n <></>\n )\n })}\n </Box>\n </div>\n )}\n </Box>\n </>\n )\n}\n\nexport const LoadingTimelinePlot = (): React.ReactNode => {\n return (\n <Box>\n <Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: '10px' }}>\n <Skeleton height=\"45px\" width=\"80px\" />\n <Skeleton height=\"45px\" width=\"80px\" />\n </Box>\n <Box sx={{ display: 'flex' }}>\n <Skeleton height=\"150px\" width=\"100%\" />\n </Box>\n </Box>\n )\n}\n\nexport default TimelinePlot\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimelinePlotSpeciesSelector.js","names":[],"sources":["../../../src/components/TimelinePlot/TimelinePlotSpeciesSelector.tsx"],"sourcesContent":["import React from 'react'\nimport { StyledFormControl } from '@/components/styled'\nimport { useGetFullTableQueryResults } from '@/synapse-queries'\nimport { parseEntityIdFromSqlStatement } from '@/utils/functions'\nimport { BUNDLE_MASK_QUERY_RESULTS } from '@/utils/SynapseConstants'\nimport { InputLabel, MenuItem, Select } from '@mui/material'\nimport { Box } from '@mui/system'\nimport { QueryFilter } from '@sage-bionetworks/synapse-types'\nimport { Dispatch, SetStateAction } from 'react'\n\nexport type TimelinePlotSpeciesSelectorProps = {\n sql: string\n additionalFilters?: QueryFilter[]\n species?: string | null\n setSpecies: Dispatch<SetStateAction<string | null | undefined>>\n}\nexport const TimelinePlotSpeciesSelector = ({\n sql,\n additionalFilters,\n species,\n setSpecies,\n}: TimelinePlotSpeciesSelectorProps): React.ReactNode => {\n const eventsTableId = parseEntityIdFromSqlStatement(sql)\n // Fetch the species\n const eventTableQuery = useGetFullTableQueryResults({\n entityId: eventsTableId,\n query: {\n sql: `SELECT distinct unnest(species) FROM ${eventsTableId} WHERE species IS NOT NULL AND observationTime IS NOT NULL GROUP BY species`,\n additionalFilters: additionalFilters,\n },\n partMask: BUNDLE_MASK_QUERY_RESULTS,\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n })\n\n const { data: speciesData, isLoading } = eventTableQuery\n const rows = speciesData?.queryResult?.queryResults?.rows\n const defaultSpecies = rows && rows.length > 0 ? rows[0].values[0] : undefined\n if (species == undefined && defaultSpecies != undefined) {\n setSpecies(defaultSpecies)\n }\n // Hide if loading, there are no rows, or there's only 1 species option\n if (isLoading || !rows || rows.length < 2) {\n return <></>\n }\n\n return (\n <Box>\n <StyledFormControl>\n <InputLabel>Species</InputLabel>\n <Select\n sx={{ marginLeft: '2px', marginBottom: '2px' }}\n value={species}\n label=\"Species\"\n onChange={event => {\n setSpecies(event.target.value)\n }}\n >\n {rows?.map(row => {\n const species = row.values[0]!\n return (\n <MenuItem key={species} value={species}>\n {species}\n </MenuItem>\n )\n })}\n </Select>\n </StyledFormControl>\n </Box>\n )\n}\nexport default TimelinePlotSpeciesSelector\n"],"mappings":";;;;;;;;;;;;AAgBA,IAAa,KAA+B,EAC1C,QACA,sBACA,YACA,oBACuD;CACvD,IAAM,IAAgB,EAA8B,EAAI,EAYlD,EAAE,MAAM,GAAa,iBAVH,EAA4B;EAClD,UAAU;EACV,OAAO;GACL,KAAK,wCAAwC,EAAc;GACxC;GACpB;EACD,UAAA;EACA,cAAc;EACf,
|
|
1
|
+
{"version":3,"file":"TimelinePlotSpeciesSelector.js","names":[],"sources":["../../../src/components/TimelinePlot/TimelinePlotSpeciesSelector.tsx"],"sourcesContent":["import React from 'react'\nimport { StyledFormControl } from '@/components/styled'\nimport { useGetFullTableQueryResults } from '@/synapse-queries'\nimport { parseEntityIdFromSqlStatement } from '@/utils/functions'\nimport { BUNDLE_MASK_QUERY_RESULTS } from '@/utils/SynapseConstants'\nimport { InputLabel, MenuItem, Select } from '@mui/material'\nimport { Box } from '@mui/system'\nimport { QueryFilter } from '@sage-bionetworks/synapse-types'\nimport { Dispatch, SetStateAction } from 'react'\n\nexport type TimelinePlotSpeciesSelectorProps = {\n sql: string\n additionalFilters?: QueryFilter[]\n species?: string | null\n setSpecies: Dispatch<SetStateAction<string | null | undefined>>\n}\nexport const TimelinePlotSpeciesSelector = ({\n sql,\n additionalFilters,\n species,\n setSpecies,\n}: TimelinePlotSpeciesSelectorProps): React.ReactNode => {\n const eventsTableId = parseEntityIdFromSqlStatement(sql)\n // Fetch the species\n const eventTableQuery = useGetFullTableQueryResults({\n entityId: eventsTableId,\n query: {\n sql: `SELECT distinct unnest(species) FROM ${eventsTableId} WHERE species IS NOT NULL AND observationTime IS NOT NULL GROUP BY species`,\n additionalFilters: additionalFilters,\n },\n partMask: BUNDLE_MASK_QUERY_RESULTS,\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n })\n\n const { data: speciesData, isLoading } = eventTableQuery\n const rows = speciesData?.queryResult?.queryResults?.rows\n const defaultSpecies = rows && rows.length > 0 ? rows[0].values[0] : undefined\n if (species == undefined && defaultSpecies != undefined) {\n setSpecies(defaultSpecies)\n }\n // Hide if loading, there are no rows, or there's only 1 species option\n if (isLoading || !rows || rows.length < 2) {\n return <></>\n }\n\n return (\n <Box>\n <StyledFormControl>\n <InputLabel>Species</InputLabel>\n <Select\n sx={{ marginLeft: '2px', marginBottom: '2px' }}\n value={species}\n label=\"Species\"\n onChange={event => {\n setSpecies(event.target.value)\n }}\n >\n {rows?.map(row => {\n const species = row.values[0]!\n return (\n <MenuItem key={species} value={species}>\n {species}\n </MenuItem>\n )\n })}\n </Select>\n </StyledFormControl>\n </Box>\n )\n}\nexport default TimelinePlotSpeciesSelector\n"],"mappings":";;;;;;;;;;;;AAgBA,IAAa,KAA+B,EAC1C,QACA,sBACA,YACA,oBACuD;CACvD,IAAM,IAAgB,EAA8B,EAAI,EAYlD,EAAE,MAAM,GAAa,iBAVH,EAA4B;EAClD,UAAU;EACV,OAAO;GACL,KAAK,wCAAwC,EAAc;GACxC;GACpB;EACD,UAAA;EACA,cAAc;EACf,CAEwC,EACnC,IAAO,GAAa,aAAa,cAAc,MAC/C,IAAiB,KAAQ,EAAK,SAAS,IAAI,EAAK,GAAG,OAAO,KAAK,KAAA;AASrE,QARI,KAAW,QAAa,KAAkB,QAC5C,EAAW,EAAe,EAGxB,KAAa,CAAC,KAAQ,EAAK,SAAS,IAC/B,kBAAA,GAAA,EAAK,CAAA,GAIZ,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAY,WAAoB,CAAA,EAChC,kBAAC,GAAD;EACE,IAAI;GAAE,YAAY;GAAO,cAAc;GAAO;EAC9C,OAAO;EACP,OAAM;EACN,WAAU,MAAS;AACjB,KAAW,EAAM,OAAO,MAAM;;YAG/B,GAAM,KAAI,MAAO;GAChB,IAAM,IAAU,EAAI,OAAO;AAC3B,UACE,kBAAC,GAAD;IAAwB,OAAO;cAC5B;IACQ,EAFI,EAEJ;IAEb;EACK,CAAA,CACS,EAAA,CAAA,EAChB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Avatar.js","names":[],"sources":["../../../src/components/UserCard/Avatar.tsx"],"sourcesContent":["import { getColor } from '@/utils/functions/getUserData'\nimport { useOverlay } from '@/utils/hooks'\nimport { Avatar as MUIAvatar, Skeleton, SxProps, useTheme } from '@mui/material'\nimport { UserProfile } from '@sage-bionetworks/synapse-types'\nimport { CSSProperties, ReactNode, useRef } from 'react'\nimport UserCardMedium from './UserCardMedium'\n\nconst TIMER_DELAY_SHOW = 250 // milliseconds\nconst TIMER_DELAY_HIDE = 500\n\nexport type AvatarSize = 'SMALL' | 'MEDIUM' | 'LARGE'\n\nexport type AvatarProps = {\n userProfile: UserProfile\n avatarSize?: AvatarSize\n imageURL?: string\n showCardOnHover?: boolean\n isLoadingAvatar?: boolean\n className?: string\n}\n\nexport function Avatar({\n userProfile,\n avatarSize = 'LARGE',\n imageURL,\n showCardOnHover = false,\n isLoadingAvatar = false,\n className,\n}: AvatarProps) {\n const target = useRef(null)\n const theme = useTheme()\n\n const mediumUserCard = (\n <UserCardMedium userProfile={userProfile} imageURL={imageURL} />\n )\n const {\n OverlayComponent,\n isShowing: isShowingOverlay,\n toggleShow,\n toggleHide,\n } = useOverlay(mediumUserCard, target, TIMER_DELAY_SHOW, TIMER_DELAY_HIDE, {\n sx: { maxWidth: '425px' },\n })\n\n let sizeStyles: SxProps = {}\n switch (avatarSize) {\n case 'SMALL':\n sizeStyles = {\n fontSize: '12px',\n width: '20px',\n height: '20px',\n }\n break\n case 'MEDIUM':\n sizeStyles = {\n fontSize: '18px',\n width: '30px',\n height: '30px',\n }\n break\n case 'LARGE':\n sizeStyles = {\n fontSize: '26px',\n width: '80px',\n height: '80px',\n [theme.breakpoints.down('sm')]: {\n fontSize: '21px',\n width: '45px',\n height: '45px',\n },\n }\n break\n default:\n break\n }\n\n const cursorStyle = showCardOnHover ? 'pointer' : 'unset'\n\n const hasProfileImage = !!imageURL\n\n const { ownerId } = userProfile\n const { userName = ownerId, firstName } = userProfile\n const conditionalStyles: CSSProperties = hasProfileImage\n ? {\n backgroundImage: `url(${imageURL})`,\n }\n : { background: getColor(userName) }\n\n if (isLoadingAvatar) {\n return <Skeleton sx={sizeStyles} variant=\"circular\" />\n }\n\n let content: ReactNode | string = <></>\n\n if (!hasProfileImage) {\n content = firstName ? firstName[0] : userName[0]\n }\n\n return (\n <>\n {showCardOnHover && <OverlayComponent />}\n <MUIAvatar\n ref={target}\n role=\"img\"\n onMouseEnter={() => toggleShow()}\n onMouseLeave={() => toggleHide()}\n onClick={event => {\n if (showCardOnHover) {\n event.stopPropagation()\n }\n if (isShowingOverlay) {\n toggleHide(false)\n } else {\n toggleShow(false)\n }\n }}\n className={`${className ?? ''}`}\n sx={{\n backgroundPosition: 'center',\n backgroundRepeat: 'no-repeat',\n backgroundSize: 'cover',\n cursor: cursorStyle,\n textDecoration: 'none',\n ...sizeStyles,\n ...conditionalStyles,\n }}\n >\n {content}\n </MUIAvatar>\n </>\n )\n}\n"],"mappings":";;;;;;;;AAOA,IAAM,IAAmB,KACnB,IAAmB;AAazB,SAAgB,EAAO,EACrB,gBACA,gBAAa,SACb,aACA,qBAAkB,IAClB,qBAAkB,IAClB,gBACc;CACd,IAAM,IAAS,EAAO,KAAK,EACrB,IAAQ,GAAU,EAKlB,EACJ,qBACA,WAAW,GACX,eACA,kBACE,EAPF,kBAAC,GAAD;EAA6B;EAAuB;EAAY,
|
|
1
|
+
{"version":3,"file":"Avatar.js","names":[],"sources":["../../../src/components/UserCard/Avatar.tsx"],"sourcesContent":["import { getColor } from '@/utils/functions/getUserData'\nimport { useOverlay } from '@/utils/hooks'\nimport { Avatar as MUIAvatar, Skeleton, SxProps, useTheme } from '@mui/material'\nimport { UserProfile } from '@sage-bionetworks/synapse-types'\nimport { CSSProperties, ReactNode, useRef } from 'react'\nimport UserCardMedium from './UserCardMedium'\n\nconst TIMER_DELAY_SHOW = 250 // milliseconds\nconst TIMER_DELAY_HIDE = 500\n\nexport type AvatarSize = 'SMALL' | 'MEDIUM' | 'LARGE'\n\nexport type AvatarProps = {\n userProfile: UserProfile\n avatarSize?: AvatarSize\n imageURL?: string\n showCardOnHover?: boolean\n isLoadingAvatar?: boolean\n className?: string\n}\n\nexport function Avatar({\n userProfile,\n avatarSize = 'LARGE',\n imageURL,\n showCardOnHover = false,\n isLoadingAvatar = false,\n className,\n}: AvatarProps) {\n const target = useRef(null)\n const theme = useTheme()\n\n const mediumUserCard = (\n <UserCardMedium userProfile={userProfile} imageURL={imageURL} />\n )\n const {\n OverlayComponent,\n isShowing: isShowingOverlay,\n toggleShow,\n toggleHide,\n } = useOverlay(mediumUserCard, target, TIMER_DELAY_SHOW, TIMER_DELAY_HIDE, {\n sx: { maxWidth: '425px' },\n })\n\n let sizeStyles: SxProps = {}\n switch (avatarSize) {\n case 'SMALL':\n sizeStyles = {\n fontSize: '12px',\n width: '20px',\n height: '20px',\n }\n break\n case 'MEDIUM':\n sizeStyles = {\n fontSize: '18px',\n width: '30px',\n height: '30px',\n }\n break\n case 'LARGE':\n sizeStyles = {\n fontSize: '26px',\n width: '80px',\n height: '80px',\n [theme.breakpoints.down('sm')]: {\n fontSize: '21px',\n width: '45px',\n height: '45px',\n },\n }\n break\n default:\n break\n }\n\n const cursorStyle = showCardOnHover ? 'pointer' : 'unset'\n\n const hasProfileImage = !!imageURL\n\n const { ownerId } = userProfile\n const { userName = ownerId, firstName } = userProfile\n const conditionalStyles: CSSProperties = hasProfileImage\n ? {\n backgroundImage: `url(${imageURL})`,\n }\n : { background: getColor(userName) }\n\n if (isLoadingAvatar) {\n return <Skeleton sx={sizeStyles} variant=\"circular\" />\n }\n\n let content: ReactNode | string = <></>\n\n if (!hasProfileImage) {\n content = firstName ? firstName[0] : userName[0]\n }\n\n return (\n <>\n {showCardOnHover && <OverlayComponent />}\n <MUIAvatar\n ref={target}\n role=\"img\"\n onMouseEnter={() => toggleShow()}\n onMouseLeave={() => toggleHide()}\n onClick={event => {\n if (showCardOnHover) {\n event.stopPropagation()\n }\n if (isShowingOverlay) {\n toggleHide(false)\n } else {\n toggleShow(false)\n }\n }}\n className={`${className ?? ''}`}\n sx={{\n backgroundPosition: 'center',\n backgroundRepeat: 'no-repeat',\n backgroundSize: 'cover',\n cursor: cursorStyle,\n textDecoration: 'none',\n ...sizeStyles,\n ...conditionalStyles,\n }}\n >\n {content}\n </MUIAvatar>\n </>\n )\n}\n"],"mappings":";;;;;;;;AAOA,IAAM,IAAmB,KACnB,IAAmB;AAazB,SAAgB,EAAO,EACrB,gBACA,gBAAa,SACb,aACA,qBAAkB,IAClB,qBAAkB,IAClB,gBACc;CACd,IAAM,IAAS,EAAO,KAAK,EACrB,IAAQ,GAAU,EAKlB,EACJ,qBACA,WAAW,GACX,eACA,kBACE,EAPF,kBAAC,GAAD;EAA6B;EAAuB;EAAY,CAOnD,EAAgB,GAAQ,GAAkB,GAAkB,EACzE,IAAI,EAAE,UAAU,SAAS,EAC1B,CAAC,EAEE,IAAsB,EAAE;AAC5B,SAAQ,GAAR;EACE,KAAK;AACH,OAAa;IACX,UAAU;IACV,OAAO;IACP,QAAQ;IACT;AACD;EACF,KAAK;AACH,OAAa;IACX,UAAU;IACV,OAAO;IACP,QAAQ;IACT;AACD;EACF,KAAK;AACH,OAAa;IACX,UAAU;IACV,OAAO;IACP,QAAQ;KACP,EAAM,YAAY,KAAK,KAAK,GAAG;KAC9B,UAAU;KACV,OAAO;KACP,QAAQ;KACT;IACF;AACD;EACF,QACE;;CAGJ,IAAM,IAAc,IAAkB,YAAY,SAE5C,IAAkB,CAAC,CAAC,GAEpB,EAAE,eAAY,GACd,EAAE,cAAW,GAAS,iBAAc,GACpC,IAAmC,IACrC,EACE,iBAAiB,OAAO,EAAS,IAClC,GACD,EAAE,YAAY,EAAS,EAAS,EAAE;AAEtC,KAAI,EACF,QAAO,kBAAC,GAAD;EAAU,IAAI;EAAY,SAAQ;EAAa,CAAA;CAGxD,IAAI,IAA8B,kBAAA,GAAA,EAAK,CAAA;AAMvC,QAJK,MACH,IAAU,IAAY,EAAU,KAAK,EAAS,KAI9C,kBAAA,GAAA,EAAA,UAAA,CACG,KAAmB,kBAAC,GAAD,EAAoB,CAAA,EACxC,kBAAC,GAAD;EACE,KAAK;EACL,MAAK;EACL,oBAAoB,GAAY;EAChC,oBAAoB,GAAY;EAChC,UAAS,MAAS;AAIhB,GAHI,KACF,EAAM,iBAAiB,EAErB,IACF,EAAW,GAAM,GAEjB,EAAW,GAAM;;EAGrB,WAAW,GAAG,KAAa;EAC3B,IAAI;GACF,oBAAoB;GACpB,kBAAkB;GAClB,gBAAgB;GAChB,QAAQ;GACR,gBAAgB;GAChB,GAAG;GACH,GAAG;GACJ;YAEA;EACS,CAAA,CACX,EAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserCardList.js","names":[],"sources":["../../../src/components/UserCardList/UserCardList.tsx"],"sourcesContent":["import { UserProfileList } from '@/synapse-client/SynapseClient'\nimport { getUserProfileWithProfilePicAttached } from '@/utils/functions/getUserData'\nimport { MEDIUM_USER_CARD, UserCardSize } from '@/utils/SynapseConstants'\nimport {\n ColumnTypeEnum,\n RowSet,\n UserProfile,\n} from '@sage-bionetworks/synapse-types'\nimport { without } from 'lodash-es'\nimport { Component } from 'react'\nimport UserCard from '../UserCard/UserCard'\n\nexport type UserCardListProps = {\n /* The list of user IDs or null values to render. For null values, the card will be rendered with table data supplied by `firstName`, `lastName`, and `institution` columns supplied by the data prop. */\n list: (string | null)[]\n size?: UserCardSize\n // Data should not be needed, however, it gives the option to fill in a user profile with other column\n // fields. This is required specifically by AMP-AD Explore/People page\n rowSet?: RowSet\n}\n\nexport type UserCardListState = {\n userProfileMap: Record<string, UserProfile>\n}\nexport class UserCardList extends Component<\n UserCardListProps,\n UserCardListState\n> {\n constructor(props: UserCardListProps) {\n super(props)\n this.state = {\n userProfileMap: {},\n }\n this.update = this.update.bind(this)\n }\n\n componentDidMount() {\n const { list } = this.props\n this.update(list)\n }\n\n componentDidUpdate(prevProps: UserCardListProps) {\n const priorListOfIds = prevProps.list\n const newValues = without(\n this.props.list.filter(el => el),\n ...priorListOfIds,\n )\n // check that the props have changed by seeing that at least one element is different\n if (newValues.length > 0) {\n this.update(newValues)\n }\n }\n\n update(list: (string | null)[]) {\n getUserProfileWithProfilePicAttached(\n list.filter((el): el is string => !!el),\n )\n .then((data: UserProfileList) => {\n const newEntries: Record<string, UserProfile> = {}\n data.list.forEach(el => {\n const { ownerId } = el\n newEntries[ownerId] = el\n })\n this.setState({\n userProfileMap: { ...this.state.userProfileMap, ...newEntries },\n })\n })\n .catch((err: string) => {\n console.error('Error on batch call =', err)\n })\n }\n\n /**\n * Given data this will find rows where there is no userId columnType and create faux user profiles\n * using firstName, lastName, and institution (company in UserProfile object).\n * @param {RowSet} rowSet\n * @returns list of UserProfiles with firstName, lastName, company, userName (first letter of firstName) filled out.\n * @memberof UserCardList\n */\n manuallyExtractData(rowSet: RowSet) {\n const firstNameIndex = rowSet.headers.findIndex(\n el => el.name === 'firstName',\n )\n const lastNameIndex = rowSet.headers.findIndex(el => el.name === 'lastName')\n const institutionIndex = rowSet.headers.findIndex(\n el => el.name === 'institution',\n )\n const ownerId = rowSet.headers.findIndex(\n el => el.columnType === ColumnTypeEnum.USERID,\n )\n const nullOwnerIdsRows = rowSet.rows.filter(el => !el.values[ownerId])\n return nullOwnerIdsRows.map<Omit<UserProfile, 'ownerId'>>(el => {\n const values = el.values\n return {\n firstName: values[firstNameIndex] ?? '',\n lastName: values[lastNameIndex] ?? '',\n company: values[institutionIndex] ?? undefined,\n ownerId: null,\n userName: values[firstNameIndex] ? values[firstNameIndex][0] ?? '' : '',\n }\n })\n }\n\n render() {\n const { size = MEDIUM_USER_CARD, rowSet, list } = this.props\n const { userProfileMap = {} } = this.state\n const fauxUserProfilesList = rowSet && this.manuallyExtractData(rowSet)\n let fauxUserProfileIndex = 0\n return (\n <div className=\"SRC-card-grid-row\">\n {\n // we loop through the list from the props because thats the 'active set of data' whereas the data stored in state could be stale\n list.map(ownerId => {\n const userProfile = ownerId != null ? userProfileMap[ownerId] : null\n if (userProfile) {\n return (\n <div\n key={JSON.stringify(userProfile)}\n className=\"SRC-grid-item SRC-narrow-grid-item\"\n >\n <UserCard\n size={size}\n preSignedURL={userProfile.clientPreSignedURL}\n userProfile={userProfile}\n />\n </div>\n )\n }\n const fauxUserProfile =\n fauxUserProfilesList && fauxUserProfilesList[fauxUserProfileIndex]\n if (!fauxUserProfile) {\n // This could happen in one of two cases:\n // - The props just updated with a new userlist where the data is being gathered for this particular user\n // OR there is no mapping for this user\n return false\n }\n fauxUserProfileIndex += 1\n return (\n <div\n key={JSON.stringify(fauxUserProfile)}\n className=\"SRC-grid-item SRC-narrow-grid-item\"\n >\n <UserCard\n disableLink={true}\n hideEmail={true}\n size={size}\n // TODO: Modify UserCard to accept a fake profile with no ownerId field, and remove this cast.\n userProfile={fauxUserProfile as UserProfile}\n />\n </div>\n )\n })\n }\n </div>\n )\n }\n}\nexport default UserCardList\n"],"mappings":";;;;;;;;AAwBA,IAAa,IAAb,cAAkC,EAGhC;CACA,YAAY,GAA0B;AAKpC,EAJA,MAAM,EAAM,EACZ,KAAK,QAAQ,EACX,gBAAgB,EAAE,EACnB,EACD,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK;;CAGtC,oBAAoB;EAClB,IAAM,EAAE,YAAS,KAAK;AACtB,OAAK,OAAO,EAAK;;CAGnB,mBAAmB,GAA8B;EAC/C,IAAM,IAAiB,EAAU,MAC3B,IAAY,EAChB,KAAK,MAAM,KAAK,QAAO,MAAM,EAAG,EAChC,GAAG,EACJ;AAED,EAAI,EAAU,SAAS,KACrB,KAAK,OAAO,EAAU;;CAI1B,OAAO,GAAyB;AAC9B,IACE,EAAK,QAAQ,MAAqB,CAAC,CAAC,EAAG,CACxC,CACE,MAAM,MAA0B;GAC/B,IAAM,IAA0C,EAAE;AAKlD,GAJA,EAAK,KAAK,SAAQ,MAAM;IACtB,IAAM,EAAE,eAAY;AACpB,MAAW,KAAW;KACtB,EACF,KAAK,SAAS,EACZ,gBAAgB;IAAE,GAAG,KAAK,MAAM;IAAgB,GAAG;IAAY,EAChE,CAAC;IACF,CACD,OAAO,MAAgB;AACtB,WAAQ,MAAM,yBAAyB,EAAI;IAC3C;;CAUN,oBAAoB,GAAgB;EAClC,IAAM,IAAiB,EAAO,QAAQ,WACpC,MAAM,EAAG,SAAS,YACnB,EACK,IAAgB,EAAO,QAAQ,WAAU,MAAM,EAAG,SAAS,WAAW,EACtE,IAAmB,EAAO,QAAQ,WACtC,MAAM,EAAG,SAAS,cACnB,EACK,IAAU,EAAO,QAAQ,WAC7B,MAAM,EAAG,eAAe,EAAe,OACxC;AAED,SADyB,EAAO,KAAK,QAAO,MAAM,CAAC,EAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"UserCardList.js","names":[],"sources":["../../../src/components/UserCardList/UserCardList.tsx"],"sourcesContent":["import { UserProfileList } from '@/synapse-client/SynapseClient'\nimport { getUserProfileWithProfilePicAttached } from '@/utils/functions/getUserData'\nimport { MEDIUM_USER_CARD, UserCardSize } from '@/utils/SynapseConstants'\nimport {\n ColumnTypeEnum,\n RowSet,\n UserProfile,\n} from '@sage-bionetworks/synapse-types'\nimport { without } from 'lodash-es'\nimport { Component } from 'react'\nimport UserCard from '../UserCard/UserCard'\n\nexport type UserCardListProps = {\n /* The list of user IDs or null values to render. For null values, the card will be rendered with table data supplied by `firstName`, `lastName`, and `institution` columns supplied by the data prop. */\n list: (string | null)[]\n size?: UserCardSize\n // Data should not be needed, however, it gives the option to fill in a user profile with other column\n // fields. This is required specifically by AMP-AD Explore/People page\n rowSet?: RowSet\n}\n\nexport type UserCardListState = {\n userProfileMap: Record<string, UserProfile>\n}\nexport class UserCardList extends Component<\n UserCardListProps,\n UserCardListState\n> {\n constructor(props: UserCardListProps) {\n super(props)\n this.state = {\n userProfileMap: {},\n }\n this.update = this.update.bind(this)\n }\n\n componentDidMount() {\n const { list } = this.props\n this.update(list)\n }\n\n componentDidUpdate(prevProps: UserCardListProps) {\n const priorListOfIds = prevProps.list\n const newValues = without(\n this.props.list.filter(el => el),\n ...priorListOfIds,\n )\n // check that the props have changed by seeing that at least one element is different\n if (newValues.length > 0) {\n this.update(newValues)\n }\n }\n\n update(list: (string | null)[]) {\n getUserProfileWithProfilePicAttached(\n list.filter((el): el is string => !!el),\n )\n .then((data: UserProfileList) => {\n const newEntries: Record<string, UserProfile> = {}\n data.list.forEach(el => {\n const { ownerId } = el\n newEntries[ownerId] = el\n })\n this.setState({\n userProfileMap: { ...this.state.userProfileMap, ...newEntries },\n })\n })\n .catch((err: string) => {\n console.error('Error on batch call =', err)\n })\n }\n\n /**\n * Given data this will find rows where there is no userId columnType and create faux user profiles\n * using firstName, lastName, and institution (company in UserProfile object).\n * @param {RowSet} rowSet\n * @returns list of UserProfiles with firstName, lastName, company, userName (first letter of firstName) filled out.\n * @memberof UserCardList\n */\n manuallyExtractData(rowSet: RowSet) {\n const firstNameIndex = rowSet.headers.findIndex(\n el => el.name === 'firstName',\n )\n const lastNameIndex = rowSet.headers.findIndex(el => el.name === 'lastName')\n const institutionIndex = rowSet.headers.findIndex(\n el => el.name === 'institution',\n )\n const ownerId = rowSet.headers.findIndex(\n el => el.columnType === ColumnTypeEnum.USERID,\n )\n const nullOwnerIdsRows = rowSet.rows.filter(el => !el.values[ownerId])\n return nullOwnerIdsRows.map<Omit<UserProfile, 'ownerId'>>(el => {\n const values = el.values\n return {\n firstName: values[firstNameIndex] ?? '',\n lastName: values[lastNameIndex] ?? '',\n company: values[institutionIndex] ?? undefined,\n ownerId: null,\n userName: values[firstNameIndex] ? values[firstNameIndex][0] ?? '' : '',\n }\n })\n }\n\n render() {\n const { size = MEDIUM_USER_CARD, rowSet, list } = this.props\n const { userProfileMap = {} } = this.state\n const fauxUserProfilesList = rowSet && this.manuallyExtractData(rowSet)\n let fauxUserProfileIndex = 0\n return (\n <div className=\"SRC-card-grid-row\">\n {\n // we loop through the list from the props because thats the 'active set of data' whereas the data stored in state could be stale\n list.map(ownerId => {\n const userProfile = ownerId != null ? userProfileMap[ownerId] : null\n if (userProfile) {\n return (\n <div\n key={JSON.stringify(userProfile)}\n className=\"SRC-grid-item SRC-narrow-grid-item\"\n >\n <UserCard\n size={size}\n preSignedURL={userProfile.clientPreSignedURL}\n userProfile={userProfile}\n />\n </div>\n )\n }\n const fauxUserProfile =\n fauxUserProfilesList && fauxUserProfilesList[fauxUserProfileIndex]\n if (!fauxUserProfile) {\n // This could happen in one of two cases:\n // - The props just updated with a new userlist where the data is being gathered for this particular user\n // OR there is no mapping for this user\n return false\n }\n fauxUserProfileIndex += 1\n return (\n <div\n key={JSON.stringify(fauxUserProfile)}\n className=\"SRC-grid-item SRC-narrow-grid-item\"\n >\n <UserCard\n disableLink={true}\n hideEmail={true}\n size={size}\n // TODO: Modify UserCard to accept a fake profile with no ownerId field, and remove this cast.\n userProfile={fauxUserProfile as UserProfile}\n />\n </div>\n )\n })\n }\n </div>\n )\n }\n}\nexport default UserCardList\n"],"mappings":";;;;;;;;AAwBA,IAAa,IAAb,cAAkC,EAGhC;CACA,YAAY,GAA0B;AAKpC,EAJA,MAAM,EAAM,EACZ,KAAK,QAAQ,EACX,gBAAgB,EAAE,EACnB,EACD,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK;;CAGtC,oBAAoB;EAClB,IAAM,EAAE,YAAS,KAAK;AACtB,OAAK,OAAO,EAAK;;CAGnB,mBAAmB,GAA8B;EAC/C,IAAM,IAAiB,EAAU,MAC3B,IAAY,EAChB,KAAK,MAAM,KAAK,QAAO,MAAM,EAAG,EAChC,GAAG,EACJ;AAED,EAAI,EAAU,SAAS,KACrB,KAAK,OAAO,EAAU;;CAI1B,OAAO,GAAyB;AAC9B,IACE,EAAK,QAAQ,MAAqB,CAAC,CAAC,EAAG,CACxC,CACE,MAAM,MAA0B;GAC/B,IAAM,IAA0C,EAAE;AAKlD,GAJA,EAAK,KAAK,SAAQ,MAAM;IACtB,IAAM,EAAE,eAAY;AACpB,MAAW,KAAW;KACtB,EACF,KAAK,SAAS,EACZ,gBAAgB;IAAE,GAAG,KAAK,MAAM;IAAgB,GAAG;IAAY,EAChE,CAAC;IACF,CACD,OAAO,MAAgB;AACtB,WAAQ,MAAM,yBAAyB,EAAI;IAC3C;;CAUN,oBAAoB,GAAgB;EAClC,IAAM,IAAiB,EAAO,QAAQ,WACpC,MAAM,EAAG,SAAS,YACnB,EACK,IAAgB,EAAO,QAAQ,WAAU,MAAM,EAAG,SAAS,WAAW,EACtE,IAAmB,EAAO,QAAQ,WACtC,MAAM,EAAG,SAAS,cACnB,EACK,IAAU,EAAO,QAAQ,WAC7B,MAAM,EAAG,eAAe,EAAe,OACxC;AAED,SADyB,EAAO,KAAK,QAAO,MAAM,CAAC,EAAG,OAAO,GACtD,CAAiB,KAAkC,MAAM;GAC9D,IAAM,IAAS,EAAG;AAClB,UAAO;IACL,WAAW,EAAO,MAAmB;IACrC,UAAU,EAAO,MAAkB;IACnC,SAAS,EAAO,MAAqB,KAAA;IACrC,SAAS;IACT,UAAU,EAAO,KAAkB,EAAO,GAAgB,MAAM,KAAK;IACtE;IACD;;CAGJ,SAAS;EACP,IAAM,EAAE,UAAO,GAAkB,WAAQ,YAAS,KAAK,OACjD,EAAE,oBAAiB,EAAE,KAAK,KAAK,OAC/B,IAAuB,KAAU,KAAK,oBAAoB,EAAO,EACnE,IAAuB;AAC3B,SACE,kBAAC,OAAD;GAAK,WAAU;aAGX,EAAK,KAAI,MAAW;IAClB,IAAM,IAAc,KAAW,OAAiC,OAA1B,EAAe;AACrD,QAAI,EACF,QACE,kBAAC,OAAD;KAEE,WAAU;eAEV,kBAAC,GAAD;MACQ;MACN,cAAc,EAAY;MACb;MACb,CAAA;KACE,EARC,KAAK,UAAU,EAAY,CAQ5B;IAGV,IAAM,IACJ,KAAwB,EAAqB;AAQ/C,WAPK,KAML,KAAwB,GAEtB,kBAAC,OAAD;KAEE,WAAU;eAEV,kBAAC,GAAD;MACE,aAAa;MACb,WAAW;MACL;MAEN,aAAa;MACb,CAAA;KACE,EAVC,KAAK,UAAU,EAAgB,CAUhC,IAfC;KAiBT;GAEA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserCardListGroups.Mobile.js","names":[],"sources":["../../../../src/components/UserCardList/UserCardListGroups/UserCardListGroups.Mobile.tsx"],"sourcesContent":["import UserCardListRotate from '../UserCardListRotate'\nimport { FacetColumnValuesRequest } from '@sage-bionetworks/synapse-types'\nimport ExpandableContent from '../../home_page/ExpandableContent'\nimport { UserCardListGroupsProps } from './UserCardListGroups'\n\nexport default function UserCardListGroupsMobile(\n props: UserCardListGroupsProps,\n) {\n const { columnName, facetValues, ...rest } = props\n return (\n <div>\n {facetValues?.map((el, curIndex) => {\n const facetValue = facetValues[curIndex]\n const selectedFacet: FacetColumnValuesRequest = {\n columnName,\n facetValues: [facetValue],\n concreteType:\n 'org.sagebionetworks.repo.model.table.FacetColumnValuesRequest',\n }\n const content = (\n <UserCardListRotate {...rest} selectedFacets={[selectedFacet]} />\n )\n const title = <> {facetValue} </>\n return (\n <ExpandableContent\n key={`UserCardListGroup-Mobile-${curIndex}`}\n title={title}\n content={content}\n />\n )\n })}\n </div>\n )\n}\n"],"mappings":";;;;AAKA,SAAwB,EACtB,GACA;CACA,IAAM,EAAE,eAAY,gBAAa,GAAG,MAAS;AAC7C,QACE,kBAAC,OAAD,EAAA,UACG,GAAa,KAAK,GAAI,MAAa;EAClC,IAAM,IAAa,EAAY,IACzB,IAA0C;GAC9C;GACA,aAAa,CAAC,EAAW;GACzB,cACE;GACH,EACK,IACJ,kBAAC,GAAD;GAAoB,GAAI;GAAM,gBAAgB,CAAC,EAAc;GAAI,CAAA;AAGnE,SACE,kBAAC,GAAD;GAES,OAJG,kBAAA,GAAA,EAAA,UAAA;IAAE;IAAE;IAAW;IAAI,EAAA,
|
|
1
|
+
{"version":3,"file":"UserCardListGroups.Mobile.js","names":[],"sources":["../../../../src/components/UserCardList/UserCardListGroups/UserCardListGroups.Mobile.tsx"],"sourcesContent":["import UserCardListRotate from '../UserCardListRotate'\nimport { FacetColumnValuesRequest } from '@sage-bionetworks/synapse-types'\nimport ExpandableContent from '../../home_page/ExpandableContent'\nimport { UserCardListGroupsProps } from './UserCardListGroups'\n\nexport default function UserCardListGroupsMobile(\n props: UserCardListGroupsProps,\n) {\n const { columnName, facetValues, ...rest } = props\n return (\n <div>\n {facetValues?.map((el, curIndex) => {\n const facetValue = facetValues[curIndex]\n const selectedFacet: FacetColumnValuesRequest = {\n columnName,\n facetValues: [facetValue],\n concreteType:\n 'org.sagebionetworks.repo.model.table.FacetColumnValuesRequest',\n }\n const content = (\n <UserCardListRotate {...rest} selectedFacets={[selectedFacet]} />\n )\n const title = <> {facetValue} </>\n return (\n <ExpandableContent\n key={`UserCardListGroup-Mobile-${curIndex}`}\n title={title}\n content={content}\n />\n )\n })}\n </div>\n )\n}\n"],"mappings":";;;;AAKA,SAAwB,EACtB,GACA;CACA,IAAM,EAAE,eAAY,gBAAa,GAAG,MAAS;AAC7C,QACE,kBAAC,OAAD,EAAA,UACG,GAAa,KAAK,GAAI,MAAa;EAClC,IAAM,IAAa,EAAY,IACzB,IAA0C;GAC9C;GACA,aAAa,CAAC,EAAW;GACzB,cACE;GACH,EACK,IACJ,kBAAC,GAAD;GAAoB,GAAI;GAAM,gBAAgB,CAAC,EAAc;GAAI,CAAA;AAGnE,SACE,kBAAC,GAAD;GAES,OAJG,kBAAA,GAAA,EAAA,UAAA;IAAE;IAAE;IAAW;IAAI,EAAA,CAItB;GACE;GACT,EAHK,4BAA4B,IAGjC;GAEJ,EACE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserCardListRotate.js","names":[],"sources":["../../../src/components/UserCardList/UserCardListRotate.tsx"],"sourcesContent":["import LargeButton from '@/components/styled/LargeButton'\nimport SynapseClient from '@/synapse-client'\nimport { SynapseConstants } from '@/utils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport {\n getAdditionalFilters,\n parseEntityIdFromSqlStatement,\n SQLOperator,\n} from '@/utils/functions/SqlFunctions'\nimport { LARGE_USER_CARD, UserCardSize } from '@/utils/SynapseConstants'\nimport {\n ColumnTypeEnum,\n FacetColumnRequest,\n QueryBundleRequest,\n QueryResultBundle,\n} from '@sage-bionetworks/synapse-types'\nimport { useState } from 'react'\nimport { useDeepCompareEffectNoCheck } from 'use-deep-compare-effect'\nimport { LoadingUserCardMedium } from '../UserCard/UserCardMedium'\nimport UserCardList from './UserCardList'\n\nconst STORED_UID_KEY = 'sage_rotate_uids'\nconst DEFAULT_DISPLAY_COUNT = 3\n\nexport type UserCardListRotateProps = {\n sql: string\n count: number\n useQueryResultUserData?: boolean\n size?: UserCardSize\n summaryLink?: string\n summaryLinkText?: string\n selectedFacets?: FacetColumnRequest[]\n sqlOperator?: SQLOperator\n searchParams?: Record<string, string>\n additionalFiltersSessionStorageKey?: string\n}\n\nexport const getDisplayIds = (\n ids: string[] = [],\n count: number = DEFAULT_DISPLAY_COUNT,\n storageUidKey: string,\n) => {\n let storedIds: string[] = []\n let newIds: string[] = []\n const storedIdsStr = localStorage.getItem(storageUidKey)\n if (storedIdsStr != null) {\n storedIds = JSON.parse(storedIdsStr)\n }\n if (!storedIds.length) {\n // no stuff in storage\n newIds = ids.slice(0, count)\n localStorage.setItem(storageUidKey, JSON.stringify(newIds))\n return newIds\n } else {\n // has stuff in storage\n const filtered = ids.filter(item => !storedIds.includes(item))\n if (filtered.length >= count) {\n newIds = filtered.slice(0, count)\n localStorage.setItem(\n storageUidKey,\n JSON.stringify(storedIds.concat(newIds)),\n )\n return newIds\n } else {\n localStorage.removeItem(storageUidKey)\n const part = ids.slice(0, count - filtered.length)\n localStorage.setItem(storageUidKey, JSON.stringify(part))\n return filtered.concat(part)\n }\n }\n}\n\nexport function UserCardListRotate({\n sql,\n count,\n useQueryResultUserData = false,\n size = LARGE_USER_CARD,\n summaryLink,\n summaryLinkText,\n selectedFacets,\n searchParams,\n sqlOperator,\n additionalFiltersSessionStorageKey,\n}: UserCardListRotateProps) {\n const { accessToken } = useSynapseContext()\n const [userIds, setUserIds] = useState<string[]>([])\n const [queryData, setQueryData] = useState<QueryResultBundle>()\n const [isLoading, setIsLoading] = useState<boolean>()\n let mounted = true\n const storageUidKey = `${STORED_UID_KEY}-${sql}-${JSON.stringify(\n selectedFacets,\n )}`\n useDeepCompareEffectNoCheck(() => {\n const fetchData = async function () {\n setIsLoading(true)\n const entityId = parseEntityIdFromSqlStatement(sql)\n const additionalFilters = getAdditionalFilters(\n searchParams,\n sqlOperator,\n additionalFiltersSessionStorageKey,\n )\n const partMask = SynapseConstants.BUNDLE_MASK_QUERY_RESULTS\n const request: QueryBundleRequest = {\n partMask,\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId,\n query: {\n sql,\n additionalFilters,\n selectedFacets,\n },\n }\n\n const queryResultBundle = await SynapseClient.getFullQueryTableResults(\n request,\n accessToken,\n )\n const { queryResult } = queryResultBundle\n if (queryResult?.queryResults.rows) {\n // find the column that has the USER_ID in it.\n const ownerIdColumnIndex = queryResult.queryResults.headers.findIndex(\n el => el.columnType === ColumnTypeEnum.USERID,\n )\n const ids: string[] = queryResult.queryResults.rows\n .map(d => d.values[ownerIdColumnIndex])\n .filter((id): id is string => id !== null)\n if (mounted) {\n const newIds = getDisplayIds(ids, count, storageUidKey)\n setUserIds(newIds)\n if (useQueryResultUserData) {\n setQueryData(queryResultBundle)\n }\n setIsLoading(false)\n }\n } else {\n console.log('UserCardListRotate: Error getting data')\n }\n }\n fetchData()\n\n return () => {\n mounted = false\n }\n }, [sql, selectedFacets, count, accessToken, searchParams, sqlOperator])\n\n return (\n <div className=\"UserCardListRotate\">\n {isLoading && <LoadingUserCardMedium />}\n {!isLoading && userIds.length === 0 && (\n <p className=\"font-italic\">No one was found.</p>\n )}\n {!isLoading && userIds.length > 0 && (\n <UserCardList\n list={userIds}\n size={size}\n rowSet={queryData?.queryResult?.queryResults}\n />\n )}\n {summaryLink && summaryLinkText && (\n <div className=\"UserCardListRotate__summary\">\n <LargeButton color=\"secondary\" variant=\"contained\" href={summaryLink}>\n {summaryLinkText}\n </LargeButton>\n </div>\n )}\n </div>\n )\n}\n\nexport default UserCardListRotate\n"],"mappings":";;;;;;;;;;;;;AAqBA,IAAM,IAAiB,oBACjB,IAAwB,GAejB,KACX,IAAgB,EAAE,EAClB,IAAgB,GAChB,MACG;CACH,IAAI,IAAsB,EAAE,EACxB,IAAmB,EAAE,EACnB,IAAe,aAAa,QAAQ,EAAc;AAIxD,KAHI,KAAgB,SAClB,IAAY,KAAK,MAAM,EAAa,GAEjC,EAAU,QAKR;EAEL,IAAM,IAAW,EAAI,QAAO,MAAQ,CAAC,EAAU,SAAS,EAAK,CAAC;AAC9D,MAAI,EAAS,UAAU,EAMrB,QALA,IAAS,EAAS,MAAM,GAAG,EAAM,EACjC,aAAa,QACX,GACA,KAAK,UAAU,EAAU,OAAO,EAAO,CAAC,CACzC,EACM;EACF;AACL,gBAAa,WAAW,EAAc;GACtC,IAAM,IAAO,EAAI,MAAM,GAAG,IAAQ,EAAS,OAAO;AAElD,UADA,aAAa,QAAQ,GAAe,KAAK,UAAU,EAAK,CAAC,EAClD,EAAS,OAAO,EAAK;;OAf9B,QAFA,IAAS,EAAI,MAAM,GAAG,EAAM,EAC5B,aAAa,QAAQ,GAAe,KAAK,UAAU,EAAO,CAAC,EACpD;;AAoBX,SAAgB,EAAmB,EACjC,QACA,UACA,4BAAyB,IACzB,UAAO,GACP,gBACA,oBACA,mBACA,iBACA,gBACA,yCAC0B;CAC1B,IAAM,EAAE,mBAAgB,GAAmB,EACrC,CAAC,GAAS,KAAc,EAAmB,EAAE,CAAC,EAC9C,CAAC,GAAW,KAAgB,GAA6B,EACzD,CAAC,GAAW,KAAgB,GAAmB,EACjD,IAAU,IACR,IAAgB,GAAG,EAAe,GAAG,EAAI,GAAG,KAAK,UACrD,EACD;AAsDD,QArDA,
|
|
1
|
+
{"version":3,"file":"UserCardListRotate.js","names":[],"sources":["../../../src/components/UserCardList/UserCardListRotate.tsx"],"sourcesContent":["import LargeButton from '@/components/styled/LargeButton'\nimport SynapseClient from '@/synapse-client'\nimport { SynapseConstants } from '@/utils'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport {\n getAdditionalFilters,\n parseEntityIdFromSqlStatement,\n SQLOperator,\n} from '@/utils/functions/SqlFunctions'\nimport { LARGE_USER_CARD, UserCardSize } from '@/utils/SynapseConstants'\nimport {\n ColumnTypeEnum,\n FacetColumnRequest,\n QueryBundleRequest,\n QueryResultBundle,\n} from '@sage-bionetworks/synapse-types'\nimport { useState } from 'react'\nimport { useDeepCompareEffectNoCheck } from 'use-deep-compare-effect'\nimport { LoadingUserCardMedium } from '../UserCard/UserCardMedium'\nimport UserCardList from './UserCardList'\n\nconst STORED_UID_KEY = 'sage_rotate_uids'\nconst DEFAULT_DISPLAY_COUNT = 3\n\nexport type UserCardListRotateProps = {\n sql: string\n count: number\n useQueryResultUserData?: boolean\n size?: UserCardSize\n summaryLink?: string\n summaryLinkText?: string\n selectedFacets?: FacetColumnRequest[]\n sqlOperator?: SQLOperator\n searchParams?: Record<string, string>\n additionalFiltersSessionStorageKey?: string\n}\n\nexport const getDisplayIds = (\n ids: string[] = [],\n count: number = DEFAULT_DISPLAY_COUNT,\n storageUidKey: string,\n) => {\n let storedIds: string[] = []\n let newIds: string[] = []\n const storedIdsStr = localStorage.getItem(storageUidKey)\n if (storedIdsStr != null) {\n storedIds = JSON.parse(storedIdsStr)\n }\n if (!storedIds.length) {\n // no stuff in storage\n newIds = ids.slice(0, count)\n localStorage.setItem(storageUidKey, JSON.stringify(newIds))\n return newIds\n } else {\n // has stuff in storage\n const filtered = ids.filter(item => !storedIds.includes(item))\n if (filtered.length >= count) {\n newIds = filtered.slice(0, count)\n localStorage.setItem(\n storageUidKey,\n JSON.stringify(storedIds.concat(newIds)),\n )\n return newIds\n } else {\n localStorage.removeItem(storageUidKey)\n const part = ids.slice(0, count - filtered.length)\n localStorage.setItem(storageUidKey, JSON.stringify(part))\n return filtered.concat(part)\n }\n }\n}\n\nexport function UserCardListRotate({\n sql,\n count,\n useQueryResultUserData = false,\n size = LARGE_USER_CARD,\n summaryLink,\n summaryLinkText,\n selectedFacets,\n searchParams,\n sqlOperator,\n additionalFiltersSessionStorageKey,\n}: UserCardListRotateProps) {\n const { accessToken } = useSynapseContext()\n const [userIds, setUserIds] = useState<string[]>([])\n const [queryData, setQueryData] = useState<QueryResultBundle>()\n const [isLoading, setIsLoading] = useState<boolean>()\n let mounted = true\n const storageUidKey = `${STORED_UID_KEY}-${sql}-${JSON.stringify(\n selectedFacets,\n )}`\n useDeepCompareEffectNoCheck(() => {\n const fetchData = async function () {\n setIsLoading(true)\n const entityId = parseEntityIdFromSqlStatement(sql)\n const additionalFilters = getAdditionalFilters(\n searchParams,\n sqlOperator,\n additionalFiltersSessionStorageKey,\n )\n const partMask = SynapseConstants.BUNDLE_MASK_QUERY_RESULTS\n const request: QueryBundleRequest = {\n partMask,\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId,\n query: {\n sql,\n additionalFilters,\n selectedFacets,\n },\n }\n\n const queryResultBundle = await SynapseClient.getFullQueryTableResults(\n request,\n accessToken,\n )\n const { queryResult } = queryResultBundle\n if (queryResult?.queryResults.rows) {\n // find the column that has the USER_ID in it.\n const ownerIdColumnIndex = queryResult.queryResults.headers.findIndex(\n el => el.columnType === ColumnTypeEnum.USERID,\n )\n const ids: string[] = queryResult.queryResults.rows\n .map(d => d.values[ownerIdColumnIndex])\n .filter((id): id is string => id !== null)\n if (mounted) {\n const newIds = getDisplayIds(ids, count, storageUidKey)\n setUserIds(newIds)\n if (useQueryResultUserData) {\n setQueryData(queryResultBundle)\n }\n setIsLoading(false)\n }\n } else {\n console.log('UserCardListRotate: Error getting data')\n }\n }\n fetchData()\n\n return () => {\n mounted = false\n }\n }, [sql, selectedFacets, count, accessToken, searchParams, sqlOperator])\n\n return (\n <div className=\"UserCardListRotate\">\n {isLoading && <LoadingUserCardMedium />}\n {!isLoading && userIds.length === 0 && (\n <p className=\"font-italic\">No one was found.</p>\n )}\n {!isLoading && userIds.length > 0 && (\n <UserCardList\n list={userIds}\n size={size}\n rowSet={queryData?.queryResult?.queryResults}\n />\n )}\n {summaryLink && summaryLinkText && (\n <div className=\"UserCardListRotate__summary\">\n <LargeButton color=\"secondary\" variant=\"contained\" href={summaryLink}>\n {summaryLinkText}\n </LargeButton>\n </div>\n )}\n </div>\n )\n}\n\nexport default UserCardListRotate\n"],"mappings":";;;;;;;;;;;;;AAqBA,IAAM,IAAiB,oBACjB,IAAwB,GAejB,KACX,IAAgB,EAAE,EAClB,IAAgB,GAChB,MACG;CACH,IAAI,IAAsB,EAAE,EACxB,IAAmB,EAAE,EACnB,IAAe,aAAa,QAAQ,EAAc;AAIxD,KAHI,KAAgB,SAClB,IAAY,KAAK,MAAM,EAAa,GAEjC,EAAU,QAKR;EAEL,IAAM,IAAW,EAAI,QAAO,MAAQ,CAAC,EAAU,SAAS,EAAK,CAAC;AAC9D,MAAI,EAAS,UAAU,EAMrB,QALA,IAAS,EAAS,MAAM,GAAG,EAAM,EACjC,aAAa,QACX,GACA,KAAK,UAAU,EAAU,OAAO,EAAO,CAAC,CACzC,EACM;EACF;AACL,gBAAa,WAAW,EAAc;GACtC,IAAM,IAAO,EAAI,MAAM,GAAG,IAAQ,EAAS,OAAO;AAElD,UADA,aAAa,QAAQ,GAAe,KAAK,UAAU,EAAK,CAAC,EAClD,EAAS,OAAO,EAAK;;OAf9B,QAFA,IAAS,EAAI,MAAM,GAAG,EAAM,EAC5B,aAAa,QAAQ,GAAe,KAAK,UAAU,EAAO,CAAC,EACpD;;AAoBX,SAAgB,EAAmB,EACjC,QACA,UACA,4BAAyB,IACzB,UAAO,GACP,gBACA,oBACA,mBACA,iBACA,gBACA,yCAC0B;CAC1B,IAAM,EAAE,mBAAgB,GAAmB,EACrC,CAAC,GAAS,KAAc,EAAmB,EAAE,CAAC,EAC9C,CAAC,GAAW,KAAgB,GAA6B,EACzD,CAAC,GAAW,KAAgB,GAAmB,EACjD,IAAU,IACR,IAAgB,GAAG,EAAe,GAAG,EAAI,GAAG,KAAK,UACrD,EACD;AAsDD,QArDA,SA8CE,iBA7CoC;AAClC,IAAa,GAAK;EAQlB,IAAM,IAA8B;GAClC,UAAA;GACA,cAAc;GACd,UAVe,EAA8B,EAU7C;GACA,OAAO;IACL;IACA,mBAZsB,EACxB,GACA,GACA,EASE;IACA;IACD;GACF,EAEK,IAAoB,MAAM,EAAc,yBAC5C,GACA,EACD,EACK,EAAE,mBAAgB;AACxB,MAAI,GAAa,aAAa,MAAM;GAElC,IAAM,IAAqB,EAAY,aAAa,QAAQ,WAC1D,MAAM,EAAG,eAAe,EAAe,OACxC,EACK,IAAgB,EAAY,aAAa,KAC5C,KAAI,MAAK,EAAE,OAAO,GAAoB,CACtC,QAAQ,MAAqB,MAAO,KAAK;AAC5C,GAAI,MAEF,EADe,EAAc,GAAK,GAAO,EAC9B,CAAO,EACd,KACF,EAAa,EAAkB,EAEjC,EAAa,GAAM;QAGrB,SAAQ,IAAI,yCAAyC;IAG9C,QAEE;AACX,MAAU;KAEX;EAAC;EAAK;EAAgB;EAAO;EAAa;EAAc;EAAY,CAAC,EAGtE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACG,KAAa,kBAAC,GAAD,EAAyB,CAAA;GACtC,CAAC,KAAa,EAAQ,WAAW,KAChC,kBAAC,KAAD;IAAG,WAAU;cAAc;IAAqB,CAAA;GAEjD,CAAC,KAAa,EAAQ,SAAS,KAC9B,kBAAC,GAAD;IACE,MAAM;IACA;IACN,QAAQ,GAAW,aAAa;IAChC,CAAA;GAEH,KAAe,KACd,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAa,OAAM;KAAY,SAAQ;KAAY,MAAM;eACtD;KACW,CAAA;IACV,CAAA;GAEJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUserOrTeam.js","names":[],"sources":["../../../src/components/UserOrTeamBadge/useUserOrTeam.tsx"],"sourcesContent":["import {\n useGetProfileImage,\n useGetStablePresignedUrl,\n} from '@/synapse-queries/file/useFiles'\nimport { useGetUserGroupHeader } from '@/synapse-queries/user/useUserGroupHeader'\nimport { useGetRealmPrincipals } from '@/synapse-queries/realm/useRealmPrincipals'\nimport { useGetTeam } from '@/synapse-queries/team/useTeam'\nimport { useCreateUrlForData } from '@/utils/hooks/usePreFetchResource'\nimport {\n FileHandleAssociateType,\n UserGroupHeader,\n UserProfile,\n} from '@sage-bionetworks/synapse-types'\nimport { Avatar as MUIAvatar } from '@mui/material'\nimport React, { useMemo } from 'react'\nimport { Avatar, AvatarSize } from '../UserCard/Avatar'\nimport IconSvg from '../IconSvg/IconSvg'\n\nexport type UseUserOrTeamResult = {\n /** The lightweight header for the resolved principal (user or team). Undefined while loading. */\n userGroupHeader: UserGroupHeader | undefined\n /** True while the `userGroupHeader` is being fetched. */\n isLoading: boolean\n /**\n * A stable React component that renders the icon for the resolved principal.\n *\n * - **Individual user**: renders an `Avatar` with the user's profile image, falling back to\n * colored initials when no image is available.\n * - **Regular team**: renders a team icon (`IconSvg icon=\"team\"`), or the team's custom image\n * when `Team.icon` (a file-handle ID) is set.\n * - **Public / authenticated-users group**: renders a globe icon (`IconSvg icon=\"public\"`).\n *\n * Accepts an optional `avatarSize` prop (`'SMALL' | 'MEDIUM' | 'LARGE'`, default `'SMALL'`)\n * for caller-controlled sizing. Returns `null` while the principal's identity has not yet\n * been resolved (i.e., while `isLoading` is true).\n */\n IconComponent: React.ComponentType<{ avatarSize?: AvatarSize }>\n /**\n * Identifies Synapse realm special groups.\n *\n * - `'authenticatedUsers'` — the principal is the \"All registered Synapse users\" group.\n * - `'publicGroup'` — the principal is the \"Anyone on the web\" group.\n * - `null` — the principal is an individual user, a regular team, or realm data is still loading.\n */\n specialGroupType: 'authenticatedUsers' | 'publicGroup' | null\n}\n\nfunction UserAvatarIcon({\n ownerId,\n syntheticProfile,\n avatarSize = 'SMALL',\n}: {\n ownerId: string\n syntheticProfile: UserProfile\n avatarSize?: AvatarSize\n}) {\n const { data: blob, isLoading } = useGetProfileImage(ownerId)\n const imageURL = useCreateUrlForData(blob)\n return (\n <Avatar\n userProfile={syntheticProfile}\n imageURL={imageURL}\n avatarSize={avatarSize}\n showCardOnHover={false}\n isLoadingAvatar={isLoading}\n />\n )\n}\n\nconst avatarSizePx: Record<AvatarSize, number> = {\n SMALL: 20,\n MEDIUM: 30,\n LARGE: 80,\n}\n\nfunction TeamAvatarIcon({\n ownerId,\n isPublicGroup,\n avatarSize = 'SMALL',\n}: {\n ownerId: string\n isPublicGroup: boolean\n avatarSize?: AvatarSize\n}) {\n const { data: team } = useGetTeam(ownerId, { enabled: !isPublicGroup })\n\n const { dataUrl: teamIconUrl } =\n useGetStablePresignedUrl(\n {\n fileHandleId: team?.icon ?? '',\n associateObjectId: ownerId,\n associateObjectType: FileHandleAssociateType.TeamAttachment,\n },\n false,\n { enabled: !isPublicGroup && !!team?.icon },\n ) ?? {}\n\n const size = avatarSizePx[avatarSize]\n\n if (isPublicGroup) {\n return <IconSvg icon=\"public\" wrap={false} sx={{ fontSize: size }} />\n }\n\n if (teamIconUrl) {\n return (\n <MUIAvatar\n src={teamIconUrl}\n sx={{ width: size, height: size, bgcolor: 'background.default' }}\n />\n )\n }\n\n return <IconSvg icon=\"team\" wrap={false} sx={{ fontSize: size }} />\n}\n\n/**\n * Resolves a Synapse principal (user or team) by ID and returns display data plus a\n * ready-to-render icon component.\n *\n * @param principalId - The numeric or string Synapse principal ID. If omitted, falls back to\n * `providedUserGroupHeader.ownerId`. Pass `undefined` when the ID is not yet known — the\n * hook will return `isLoading: false` with all fields undefined/null until an ID is provided.\n * @param providedUserGroupHeader - An already-fetched `UserGroupHeader`. When supplied, the\n * network fetch for the header is skipped entirely and this value is used directly. Useful\n * when the caller already has header data (e.g., from a list response).\n */\nexport function useUserOrTeam(\n principalId?: string | number,\n providedUserGroupHeader?: UserGroupHeader,\n): UseUserOrTeamResult {\n const resolvedPrincipalId = (\n principalId ??\n providedUserGroupHeader?.ownerId ??\n ''\n ).toString()\n\n const { data: fetchedUserGroupHeader, isLoading } = useGetUserGroupHeader(\n resolvedPrincipalId,\n { enabled: !providedUserGroupHeader && !!resolvedPrincipalId },\n )\n\n const userGroupHeader = providedUserGroupHeader ?? fetchedUserGroupHeader\n\n const ownerId = userGroupHeader?.ownerId\n const isIndividual = userGroupHeader?.isIndividual\n const userName = userGroupHeader?.userName\n const firstName = userGroupHeader?.firstName\n\n const { data: realmPrincipals } = useGetRealmPrincipals()\n\n const specialGroupType: 'authenticatedUsers' | 'publicGroup' | null =\n isIndividual === false\n ? realmPrincipals?.authenticatedUsers === resolvedPrincipalId\n ? 'authenticatedUsers'\n : realmPrincipals?.publicGroup === resolvedPrincipalId\n ? 'publicGroup'\n : null\n : null\n\n const isPublicGroup = specialGroupType !== null\n\n const IconComponent = useMemo<\n React.ComponentType<{\n avatarSize?: AvatarSize\n }>\n >(() => {\n if (ownerId == null || isIndividual == null) return () => null\n\n if (isIndividual) {\n const syntheticProfile = {\n ownerId,\n userName,\n firstName,\n } as unknown as UserProfile\n return ({ avatarSize }: { avatarSize?: AvatarSize }) => (\n <UserAvatarIcon\n ownerId={ownerId}\n syntheticProfile={syntheticProfile}\n avatarSize={avatarSize}\n />\n )\n } else {\n return ({ avatarSize }: { avatarSize?: AvatarSize }) => (\n <TeamAvatarIcon\n ownerId={ownerId}\n isPublicGroup={isPublicGroup}\n avatarSize={avatarSize}\n />\n )\n }\n }, [ownerId, isIndividual, userName, firstName, isPublicGroup])\n\n return { userGroupHeader, isLoading, IconComponent, specialGroupType }\n}\n"],"mappings":";;;;;;;;;;;;AA+CA,SAAS,EAAe,EACtB,YACA,qBACA,gBAAa,WAKZ;CACD,IAAM,EAAE,MAAM,GAAM,iBAAc,EAAmB,EAAQ;AAE7D,QACE,kBAAC,GAAD;EACE,aAAa;EACH,UAJG,EAAoB,
|
|
1
|
+
{"version":3,"file":"useUserOrTeam.js","names":[],"sources":["../../../src/components/UserOrTeamBadge/useUserOrTeam.tsx"],"sourcesContent":["import {\n useGetProfileImage,\n useGetStablePresignedUrl,\n} from '@/synapse-queries/file/useFiles'\nimport { useGetUserGroupHeader } from '@/synapse-queries/user/useUserGroupHeader'\nimport { useGetRealmPrincipals } from '@/synapse-queries/realm/useRealmPrincipals'\nimport { useGetTeam } from '@/synapse-queries/team/useTeam'\nimport { useCreateUrlForData } from '@/utils/hooks/usePreFetchResource'\nimport {\n FileHandleAssociateType,\n UserGroupHeader,\n UserProfile,\n} from '@sage-bionetworks/synapse-types'\nimport { Avatar as MUIAvatar } from '@mui/material'\nimport React, { useMemo } from 'react'\nimport { Avatar, AvatarSize } from '../UserCard/Avatar'\nimport IconSvg from '../IconSvg/IconSvg'\n\nexport type UseUserOrTeamResult = {\n /** The lightweight header for the resolved principal (user or team). Undefined while loading. */\n userGroupHeader: UserGroupHeader | undefined\n /** True while the `userGroupHeader` is being fetched. */\n isLoading: boolean\n /**\n * A stable React component that renders the icon for the resolved principal.\n *\n * - **Individual user**: renders an `Avatar` with the user's profile image, falling back to\n * colored initials when no image is available.\n * - **Regular team**: renders a team icon (`IconSvg icon=\"team\"`), or the team's custom image\n * when `Team.icon` (a file-handle ID) is set.\n * - **Public / authenticated-users group**: renders a globe icon (`IconSvg icon=\"public\"`).\n *\n * Accepts an optional `avatarSize` prop (`'SMALL' | 'MEDIUM' | 'LARGE'`, default `'SMALL'`)\n * for caller-controlled sizing. Returns `null` while the principal's identity has not yet\n * been resolved (i.e., while `isLoading` is true).\n */\n IconComponent: React.ComponentType<{ avatarSize?: AvatarSize }>\n /**\n * Identifies Synapse realm special groups.\n *\n * - `'authenticatedUsers'` — the principal is the \"All registered Synapse users\" group.\n * - `'publicGroup'` — the principal is the \"Anyone on the web\" group.\n * - `null` — the principal is an individual user, a regular team, or realm data is still loading.\n */\n specialGroupType: 'authenticatedUsers' | 'publicGroup' | null\n}\n\nfunction UserAvatarIcon({\n ownerId,\n syntheticProfile,\n avatarSize = 'SMALL',\n}: {\n ownerId: string\n syntheticProfile: UserProfile\n avatarSize?: AvatarSize\n}) {\n const { data: blob, isLoading } = useGetProfileImage(ownerId)\n const imageURL = useCreateUrlForData(blob)\n return (\n <Avatar\n userProfile={syntheticProfile}\n imageURL={imageURL}\n avatarSize={avatarSize}\n showCardOnHover={false}\n isLoadingAvatar={isLoading}\n />\n )\n}\n\nconst avatarSizePx: Record<AvatarSize, number> = {\n SMALL: 20,\n MEDIUM: 30,\n LARGE: 80,\n}\n\nfunction TeamAvatarIcon({\n ownerId,\n isPublicGroup,\n avatarSize = 'SMALL',\n}: {\n ownerId: string\n isPublicGroup: boolean\n avatarSize?: AvatarSize\n}) {\n const { data: team } = useGetTeam(ownerId, { enabled: !isPublicGroup })\n\n const { dataUrl: teamIconUrl } =\n useGetStablePresignedUrl(\n {\n fileHandleId: team?.icon ?? '',\n associateObjectId: ownerId,\n associateObjectType: FileHandleAssociateType.TeamAttachment,\n },\n false,\n { enabled: !isPublicGroup && !!team?.icon },\n ) ?? {}\n\n const size = avatarSizePx[avatarSize]\n\n if (isPublicGroup) {\n return <IconSvg icon=\"public\" wrap={false} sx={{ fontSize: size }} />\n }\n\n if (teamIconUrl) {\n return (\n <MUIAvatar\n src={teamIconUrl}\n sx={{ width: size, height: size, bgcolor: 'background.default' }}\n />\n )\n }\n\n return <IconSvg icon=\"team\" wrap={false} sx={{ fontSize: size }} />\n}\n\n/**\n * Resolves a Synapse principal (user or team) by ID and returns display data plus a\n * ready-to-render icon component.\n *\n * @param principalId - The numeric or string Synapse principal ID. If omitted, falls back to\n * `providedUserGroupHeader.ownerId`. Pass `undefined` when the ID is not yet known — the\n * hook will return `isLoading: false` with all fields undefined/null until an ID is provided.\n * @param providedUserGroupHeader - An already-fetched `UserGroupHeader`. When supplied, the\n * network fetch for the header is skipped entirely and this value is used directly. Useful\n * when the caller already has header data (e.g., from a list response).\n */\nexport function useUserOrTeam(\n principalId?: string | number,\n providedUserGroupHeader?: UserGroupHeader,\n): UseUserOrTeamResult {\n const resolvedPrincipalId = (\n principalId ??\n providedUserGroupHeader?.ownerId ??\n ''\n ).toString()\n\n const { data: fetchedUserGroupHeader, isLoading } = useGetUserGroupHeader(\n resolvedPrincipalId,\n { enabled: !providedUserGroupHeader && !!resolvedPrincipalId },\n )\n\n const userGroupHeader = providedUserGroupHeader ?? fetchedUserGroupHeader\n\n const ownerId = userGroupHeader?.ownerId\n const isIndividual = userGroupHeader?.isIndividual\n const userName = userGroupHeader?.userName\n const firstName = userGroupHeader?.firstName\n\n const { data: realmPrincipals } = useGetRealmPrincipals()\n\n const specialGroupType: 'authenticatedUsers' | 'publicGroup' | null =\n isIndividual === false\n ? realmPrincipals?.authenticatedUsers === resolvedPrincipalId\n ? 'authenticatedUsers'\n : realmPrincipals?.publicGroup === resolvedPrincipalId\n ? 'publicGroup'\n : null\n : null\n\n const isPublicGroup = specialGroupType !== null\n\n const IconComponent = useMemo<\n React.ComponentType<{\n avatarSize?: AvatarSize\n }>\n >(() => {\n if (ownerId == null || isIndividual == null) return () => null\n\n if (isIndividual) {\n const syntheticProfile = {\n ownerId,\n userName,\n firstName,\n } as unknown as UserProfile\n return ({ avatarSize }: { avatarSize?: AvatarSize }) => (\n <UserAvatarIcon\n ownerId={ownerId}\n syntheticProfile={syntheticProfile}\n avatarSize={avatarSize}\n />\n )\n } else {\n return ({ avatarSize }: { avatarSize?: AvatarSize }) => (\n <TeamAvatarIcon\n ownerId={ownerId}\n isPublicGroup={isPublicGroup}\n avatarSize={avatarSize}\n />\n )\n }\n }, [ownerId, isIndividual, userName, firstName, isPublicGroup])\n\n return { userGroupHeader, isLoading, IconComponent, specialGroupType }\n}\n"],"mappings":";;;;;;;;;;;;AA+CA,SAAS,EAAe,EACtB,YACA,qBACA,gBAAa,WAKZ;CACD,IAAM,EAAE,MAAM,GAAM,iBAAc,EAAmB,EAAQ;AAE7D,QACE,kBAAC,GAAD;EACE,aAAa;EACH,UAJG,EAAoB,EAIvB;EACE;EACZ,iBAAiB;EACjB,iBAAiB;EACjB,CAAA;;AAIN,IAAM,IAA2C;CAC/C,OAAO;CACP,QAAQ;CACR,OAAO;CACR;AAED,SAAS,EAAe,EACtB,YACA,kBACA,gBAAa,WAKZ;CACD,IAAM,EAAE,MAAM,MAAS,EAAW,GAAS,EAAE,SAAS,CAAC,GAAe,CAAC,EAEjE,EAAE,SAAS,MACf,EACE;EACE,cAAc,GAAM,QAAQ;EAC5B,mBAAmB;EACnB,qBAAqB,EAAwB;EAC9C,EACD,IACA,EAAE,SAAS,CAAC,KAAiB,CAAC,CAAC,GAAM,MAAM,CAC5C,IAAI,EAAE,EAEH,IAAO,EAAa;AAe1B,QAbI,IACK,kBAAC,GAAD;EAAS,MAAK;EAAS,MAAM;EAAO,IAAI,EAAE,UAAU,GAAM;EAAI,CAAA,GAGnE,IAEA,kBAAC,GAAD;EACE,KAAK;EACL,IAAI;GAAE,OAAO;GAAM,QAAQ;GAAM,SAAS;GAAsB;EAChE,CAAA,GAIC,kBAAC,GAAD;EAAS,MAAK;EAAO,MAAM;EAAO,IAAI,EAAE,UAAU,GAAM;EAAI,CAAA;;AAcrE,SAAgB,EACd,GACA,GACqB;CACrB,IAAM,KACJ,KACA,GAAyB,WACzB,IACA,UAAU,EAEN,EAAE,MAAM,GAAwB,iBAAc,EAClD,GACA,EAAE,SAAS,CAAC,KAA2B,CAAC,CAAC,GAAqB,CAC/D,EAEK,IAAkB,KAA2B,GAE7C,IAAU,GAAiB,SAC3B,IAAe,GAAiB,cAChC,IAAW,GAAiB,UAC5B,IAAY,GAAiB,WAE7B,EAAE,MAAM,MAAoB,GAAuB,EAEnD,IACJ,MAAiB,KACb,GAAiB,uBAAuB,IACtC,uBACA,GAAiB,gBAAgB,IACjC,gBACA,OACF,MAEA,IAAgB,MAAqB;AAiC3C,QAAO;EAAE;EAAiB;EAAW,eA/Bf,QAId;AACN,OAAI,KAAW,QAAQ,KAAgB,KAAM,cAAa;AAE1D,OAAI,GAAc;IAChB,IAAM,IAAmB;KACvB;KACA;KACA;KACD;AACD,YAAQ,EAAE,oBACR,kBAAC,GAAD;KACW;KACS;KACN;KACZ,CAAA;SAGJ,SAAQ,EAAE,oBACR,kBAAC,GAAD;IACW;IACM;IACH;IACZ,CAAA;KAGL;GAAC;GAAS;GAAc;GAAU;GAAW;GAAc,CAEzB;EAAe;EAAkB"}
|