synapse-react-client 4.0.21 → 4.0.23
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/assets/icons/GeneticsIcon.d.ts +3 -0
- package/dist/assets/icons/GeneticsIcon.d.ts.map +1 -0
- package/dist/assets/icons/GeneticsIcon.js +14 -0
- package/dist/assets/icons/GeneticsIcon.js.map +1 -0
- package/dist/components/CardContainer/CardContainer.d.ts.map +1 -1
- package/dist/components/CardContainer/CardContainer.js +43 -49
- package/dist/components/CardContainer/CardContainer.js.map +1 -1
- package/dist/components/CardContainerLogic/CardContainerLogic.d.ts +13 -0
- package/dist/components/CardContainerLogic/CardContainerLogic.d.ts.map +1 -1
- package/dist/components/CardContainerLogic/CardContainerLogic.js +64 -40
- package/dist/components/CardContainerLogic/CardContainerLogic.js.map +1 -1
- package/dist/components/ChallengeSubmission/ChallengeSubmissionStepper.js +1 -1
- package/dist/components/ChallengeSubmission/ChallengeSubmissionStepper.js.map +1 -1
- package/dist/components/DataExplorer/DataExplorer.css +1 -1
- package/dist/components/DataExplorer/DataExplorer.module.js +16 -16
- package/dist/components/DataExplorer/DataExplorer.module.js.map +1 -1
- package/dist/components/DataExplorer/DataExplorer.module.scss +0 -1
- package/dist/components/DataGrid/DataGrid.d.ts +1 -0
- 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/SynapseGrid.d.ts.map +1 -1
- package/dist/components/DataGrid/SynapseGrid.js +209 -206
- package/dist/components/DataGrid/SynapseGrid.js.map +1 -1
- package/dist/components/DataGrid/components/ColumnHeaderWithTooltip.d.ts +2 -1
- package/dist/components/DataGrid/components/ColumnHeaderWithTooltip.d.ts.map +1 -1
- package/dist/components/DataGrid/components/ColumnHeaderWithTooltip.js +51 -42
- package/dist/components/DataGrid/components/ColumnHeaderWithTooltip.js.map +1 -1
- package/dist/components/DataGrid/useDataGridWebsocket.d.ts.map +1 -1
- package/dist/components/DataGrid/useDataGridWebsocket.js +68 -67
- package/dist/components/DataGrid/useDataGridWebsocket.js.map +1 -1
- package/dist/components/DataGrid/utils/columnFactory.d.ts +1 -0
- package/dist/components/DataGrid/utils/columnFactory.d.ts.map +1 -1
- package/dist/components/DataGrid/utils/columnFactory.js +4 -3
- package/dist/components/DataGrid/utils/columnFactory.js.map +1 -1
- package/dist/components/DataGrid/utils/modelColsToGrid.d.ts +1 -1
- package/dist/components/DataGrid/utils/modelColsToGrid.d.ts.map +1 -1
- package/dist/components/DataGrid/utils/modelColsToGrid.js +15 -14
- package/dist/components/DataGrid/utils/modelColsToGrid.js.map +1 -1
- package/dist/components/DataGrid/utils/normalizeWebsocketError.d.ts +7 -0
- package/dist/components/DataGrid/utils/normalizeWebsocketError.d.ts.map +1 -0
- package/dist/components/DataGrid/utils/normalizeWebsocketError.js +8 -0
- package/dist/components/DataGrid/utils/normalizeWebsocketError.js.map +1 -0
- package/dist/components/EntityCitation/EntityCitation.d.ts.map +1 -1
- package/dist/components/EntityCitation/EntityCitation.js +23 -8
- package/dist/components/EntityCitation/EntityCitation.js.map +1 -1
- package/dist/components/GenericCard/DuoTermTags/DuoTermTags.d.ts +18 -0
- package/dist/components/GenericCard/DuoTermTags/DuoTermTags.d.ts.map +1 -0
- package/dist/components/GenericCard/DuoTermTags/DuoTermTags.js +112 -0
- package/dist/components/GenericCard/DuoTermTags/DuoTermTags.js.map +1 -0
- package/dist/components/GenericCard/DuoTermTags/duoTerms.d.ts +31 -0
- package/dist/components/GenericCard/DuoTermTags/duoTerms.d.ts.map +1 -0
- package/dist/components/GenericCard/DuoTermTags/duoTerms.js +198 -0
- package/dist/components/GenericCard/DuoTermTags/duoTerms.js.map +1 -0
- package/dist/components/GenericCard/SynapseCardLabel.d.ts.map +1 -1
- package/dist/components/GenericCard/SynapseCardLabel.js +4 -8
- package/dist/components/GenericCard/SynapseCardLabel.js.map +1 -1
- package/dist/components/GenericCard/TableRowGenericCard.d.ts +6 -0
- package/dist/components/GenericCard/TableRowGenericCard.d.ts.map +1 -1
- package/dist/components/GenericCard/TableRowGenericCard.js +168 -153
- package/dist/components/GenericCard/TableRowGenericCard.js.map +1 -1
- package/dist/components/Goals/Goals.d.ts +8 -0
- package/dist/components/Goals/Goals.d.ts.map +1 -1
- package/dist/components/Goals/Goals.js +54 -40
- package/dist/components/Goals/Goals.js.map +1 -1
- package/dist/components/IconSvg/IconSvg.d.ts +1 -1
- package/dist/components/IconSvg/IconSvg.d.ts.map +1 -1
- package/dist/components/IconSvg/IconSvg.js +19 -18
- package/dist/components/IconSvg/IconSvg.js.map +1 -1
- package/dist/components/ProjectVisibilityChip/ProjectVisibilityChip.d.ts +9 -0
- package/dist/components/ProjectVisibilityChip/ProjectVisibilityChip.d.ts.map +1 -0
- package/dist/components/ProjectVisibilityChip/ProjectVisibilityChip.js +69 -0
- package/dist/components/ProjectVisibilityChip/ProjectVisibilityChip.js.map +1 -0
- package/dist/components/ProjectVisibilityChip/getProjectVisibilityFromACL.d.ts +13 -0
- package/dist/components/ProjectVisibilityChip/getProjectVisibilityFromACL.d.ts.map +1 -0
- package/dist/components/ProjectVisibilityChip/getProjectVisibilityFromACL.js +11 -0
- package/dist/components/ProjectVisibilityChip/getProjectVisibilityFromACL.js.map +1 -0
- package/dist/components/ProjectVisibilityChip/index.d.ts +5 -0
- package/dist/components/ProjectVisibilityChip/index.d.ts.map +1 -0
- package/dist/components/ProjectVisibilityChip/index.js +7 -0
- package/dist/components/ProjectVisibilityChip/index.js.map +1 -0
- package/dist/components/QueryVisualizationWrapper/QueryVisualizationContext.d.ts +21 -0
- package/dist/components/QueryVisualizationWrapper/QueryVisualizationContext.d.ts.map +1 -1
- package/dist/components/QueryVisualizationWrapper/QueryVisualizationContext.js.map +1 -1
- package/dist/components/QueryVisualizationWrapper/QueryVisualizationWrapper.d.ts +10 -0
- package/dist/components/QueryVisualizationWrapper/QueryVisualizationWrapper.d.ts.map +1 -1
- package/dist/components/QueryVisualizationWrapper/QueryVisualizationWrapper.js +66 -60
- package/dist/components/QueryVisualizationWrapper/QueryVisualizationWrapper.js.map +1 -1
- package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.d.ts +1 -1
- package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.d.ts.map +1 -1
- package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.js +2 -0
- package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.js.map +1 -1
- package/dist/components/QueryWrapperPlotNav/RowSetView.d.ts.map +1 -1
- package/dist/components/QueryWrapperPlotNav/RowSetView.js +9 -12
- package/dist/components/QueryWrapperPlotNav/RowSetView.js.map +1 -1
- package/dist/components/RssFeedCards/RssFeedCards.d.ts +7 -15
- package/dist/components/RssFeedCards/RssFeedCards.d.ts.map +1 -1
- package/dist/components/RssFeedCards/RssFeedCards.js +114 -123
- package/dist/components/RssFeedCards/RssFeedCards.js.map +1 -1
- package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.d.ts +33 -6
- package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.d.ts.map +1 -1
- package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.js +93 -55
- package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.js.map +1 -1
- package/dist/components/SearchQueryWrapper/SearchQueryWrapper.d.ts +3 -0
- package/dist/components/SearchQueryWrapper/SearchQueryWrapper.d.ts.map +1 -1
- package/dist/components/SearchQueryWrapper/SearchQueryWrapper.js +39 -37
- package/dist/components/SearchQueryWrapper/SearchQueryWrapper.js.map +1 -1
- package/dist/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.d.ts +3 -1
- package/dist/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.d.ts.map +1 -1
- package/dist/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.js +4 -0
- package/dist/components/SearchQueryWrapperPlotNav/SearchQueryWrapperPlotNav.js.map +1 -1
- package/dist/components/UserCardList/UserCardList.d.ts +2 -20
- package/dist/components/UserCardList/UserCardList.d.ts.map +1 -1
- package/dist/components/UserCardList/UserCardList.js +49 -72
- package/dist/components/UserCardList/UserCardList.js.map +1 -1
- package/dist/components/UserCardList/UserCardListRotate.d.ts +13 -0
- package/dist/components/UserCardList/UserCardListRotate.d.ts.map +1 -1
- package/dist/components/UserCardList/UserCardListRotate.js +82 -57
- package/dist/components/UserCardList/UserCardListRotate.js.map +1 -1
- package/dist/components/entity/page/action_menu/EntityActionMenu.d.ts +5 -2
- package/dist/components/entity/page/action_menu/EntityActionMenu.d.ts.map +1 -1
- package/dist/components/entity/page/action_menu/EntityActionMenu.js +68 -51
- package/dist/components/entity/page/action_menu/EntityActionMenu.js.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/menu/ComplexMenu.d.ts +8 -1
- package/dist/components/menu/ComplexMenu.d.ts.map +1 -1
- package/dist/components/menu/ComplexMenu.js +50 -20
- package/dist/components/menu/ComplexMenu.js.map +1 -1
- package/dist/components/widgets/facet-nav/FacetValueChip.d.ts +25 -0
- package/dist/components/widgets/facet-nav/FacetValueChip.d.ts.map +1 -0
- package/dist/components/widgets/facet-nav/FacetValueChip.js +40 -0
- package/dist/components/widgets/facet-nav/FacetValueChip.js.map +1 -0
- package/dist/components/widgets/facet-nav/SelectionCriteriaPill.d.ts +10 -5
- package/dist/components/widgets/facet-nav/SelectionCriteriaPill.d.ts.map +1 -1
- package/dist/components/widgets/facet-nav/SelectionCriteriaPill.js +10 -17
- package/dist/components/widgets/facet-nav/SelectionCriteriaPill.js.map +1 -1
- package/dist/components/widgets/facet-nav/SelectionCriteriaPills.d.ts.map +1 -1
- package/dist/components/widgets/facet-nav/SelectionCriteriaPills.js +11 -10
- package/dist/components/widgets/facet-nav/SelectionCriteriaPills.js.map +1 -1
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.d.ts.map +1 -1
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.js +70 -54
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.js.map +1 -1
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterOption.d.ts +2 -1
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterOption.d.ts.map +1 -1
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterOption.js +17 -1
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterOption.js.map +1 -1
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterUI.d.ts +2 -0
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterUI.d.ts.map +1 -1
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterUI.js +7 -7
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilterUI.js.map +1 -1
- package/dist/features/curator/dashboard/components/CurationTaskCard.d.ts.map +1 -1
- package/dist/features/curator/dashboard/components/CurationTaskCard.js +99 -45
- package/dist/features/curator/dashboard/components/CurationTaskCard.js.map +1 -1
- package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.d.ts.map +1 -1
- package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.js +174 -149
- package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.js.map +1 -1
- package/dist/features/entity/metadata-task/utils/constants.d.ts +6 -0
- package/dist/features/entity/metadata-task/utils/constants.d.ts.map +1 -1
- package/dist/features/entity/metadata-task/utils/constants.js +19 -2
- package/dist/features/entity/metadata-task/utils/constants.js.map +1 -1
- package/dist/style/abstracts/_cssVariables.scss +1 -0
- package/dist/style/components/_total-query-results.scss +10 -34
- package/dist/style/main.css +1 -1
- package/dist/synapse-queries/KeyFactory.d.ts +1 -0
- package/dist/synapse-queries/KeyFactory.d.ts.map +1 -1
- package/dist/synapse-queries/KeyFactory.js +3 -0
- package/dist/synapse-queries/KeyFactory.js.map +1 -1
- package/dist/synapse-queries/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/index.js +46 -44
- package/dist/synapse-queries/user/index.js +3 -3
- package/dist/synapse-queries/user/useUserBundle.d.ts +12 -0
- package/dist/synapse-queries/user/useUserBundle.d.ts.map +1 -1
- package/dist/synapse-queries/user/useUserBundle.js +50 -36
- package/dist/synapse-queries/user/useUserBundle.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/AppUtils/session/SessionInitializedGuard.d.ts +10 -5
- package/dist/utils/AppUtils/session/SessionInitializedGuard.d.ts.map +1 -1
- package/dist/utils/AppUtils/session/SessionInitializedGuard.js +13 -9
- package/dist/utils/AppUtils/session/SessionInitializedGuard.js.map +1 -1
- package/dist/utils/functions/SqlFunctions.d.ts +1 -1
- package/dist/utils/functions/SqlFunctions.d.ts.map +1 -1
- package/dist/utils/functions/SqlFunctions.js +1 -1
- package/dist/utils/functions/SqlFunctions.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDataGridWebsocket.js","names":["GridModel","useCRDTModelView","useCallback","useEffect","useMemo","useReducer","useRef","DataGridWebSocket","useEstablishWebsocketConnection","useDocumentVisibility","WebSocketState","model","hasCompletedInitialSync","isSyncing","isConnected","isConnecting","connectionParams","replicaId","sessionId","websocketInstance","connectionAttemptId","connectionError","websocketError","WebSocketAction","type","payload","attemptId","websocketReducer","state","action","isSameConnection","initialWebSocketState","UseDataGridWebSocketOptions","onGridReady","onReplicaConnected","onReplicaDisconnected","useDataGridWebSocket","options","dispatch","connectionAttemptCounter","activeConnectionAttemptIdRef","modelSnapshot","isDocumentVisible","mutateAsync","establishWebsocketConnection","isPending","isEstablishingWebsocketConnection","error","errorEstablishingWebsocketConnection","presignedUrl","reset","resetEstablishWebsocketConnection","clearPresignedUrl","handleModelCreate","newModel","websocketOptionsWithoutModel","onStatusChange","open","onModelCreate","onSyncStart","onSyncEnd","onError","connect","isDifferentConnection","current","websocketOptions","then","ws","disconnect","catch","err","console","previousConnectionParams","previous","isModelRenderable","api","getSnapshot","columnNames","columnOrder","rows","columnsReady","length","orderReady","rowsReady","hasSufficientData"],"sources":["../../../src/components/DataGrid/useDataGridWebsocket.ts"],"sourcesContent":["import { GridModel } from '@/components/DataGrid/DataGridTypes'\nimport { useCRDTModelView } from '@/components/DataGrid/useCRDTModelView'\nimport { useCallback, useEffect, useMemo, useReducer, useRef } from 'react'\nimport { DataGridWebSocket } from './DataGridWebSocket'\nimport { useEstablishWebsocketConnection } from '@/synapse-queries/grid/useEstablishWebsocketConnection'\nimport { useDocumentVisibility } from '@react-hookz/web'\n\n// State type\ninterface WebSocketState {\n model: GridModel | null\n /**\n * True if the WebSocket has finished the initial sync and can process CRDT updates.\n * Corresponds to the `GRID_READY` action.\n */\n hasCompletedInitialSync: boolean\n /**\n * True while a clock-sync exchange is in progress. Set on every outgoing\n * `synchronize-clock` and cleared on `ResponseComplete`, which the server\n * sends once per exchange to signal \"we're synchronized\" — not per request.\n */\n isSyncing: boolean\n isConnected: boolean\n isConnecting: boolean\n connectionParams: {\n replicaId: number\n sessionId: string\n } | null\n websocketInstance: DataGridWebSocket | null\n connectionAttemptId: number | null\n connectionError: unknown\n websocketError: unknown\n}\n\n// Action types\ntype WebSocketAction =\n | {\n type: 'CONNECT_REQUESTED'\n payload: { replicaId: number; sessionId: string; attemptId: number }\n }\n | { type: 'CONNECTION_ESTABLISHED'; payload: DataGridWebSocket }\n | { type: 'CONNECTION_OPENED' }\n | { type: 'CONNECTION_CLOSED' }\n | { type: 'GRID_READY' }\n | { type: 'SYNC_STARTED' }\n | { type: 'SYNC_ENDED' }\n | { type: 'MODEL_CREATED'; payload: GridModel }\n | { type: 'CONNECTION_ERROR'; payload: unknown }\n | { type: 'WEBSOCKET_ERROR'; payload: unknown }\n\n// Reducer function\nfunction websocketReducer(\n state: WebSocketState,\n action: WebSocketAction,\n): WebSocketState {\n switch (action.type) {\n case 'CONNECT_REQUESTED': {\n const isSameConnection =\n state.connectionParams &&\n state.connectionParams.replicaId === action.payload.replicaId &&\n state.connectionParams.sessionId === action.payload.sessionId\n\n return {\n ...state,\n connectionParams: {\n replicaId: action.payload.replicaId,\n sessionId: action.payload.sessionId,\n },\n hasCompletedInitialSync: isSameConnection\n ? state.hasCompletedInitialSync\n : false,\n isSyncing: false,\n model: isSameConnection ? state.model : null,\n isConnected: false,\n isConnecting: false,\n connectionAttemptId: action.payload.attemptId,\n connectionError: null,\n }\n }\n\n case 'CONNECTION_ESTABLISHED':\n return {\n ...state,\n websocketInstance: action.payload,\n isConnecting: true,\n }\n\n case 'CONNECTION_OPENED':\n return {\n ...state,\n isConnected: true,\n isConnecting: false,\n }\n\n case 'CONNECTION_CLOSED':\n return {\n ...state,\n isConnected: false,\n isConnecting: false,\n isSyncing: false,\n }\n\n case 'GRID_READY':\n return {\n ...state,\n hasCompletedInitialSync: true,\n }\n\n case 'SYNC_STARTED':\n return {\n ...state,\n isSyncing: true,\n }\n\n case 'SYNC_ENDED':\n return {\n ...state,\n isSyncing: false,\n }\n\n case 'MODEL_CREATED':\n return {\n ...state,\n model: action.payload,\n }\n\n case 'CONNECTION_ERROR':\n return {\n ...state,\n connectionError: action.payload,\n isConnecting: false,\n }\n\n case 'WEBSOCKET_ERROR':\n return {\n ...state,\n websocketError: action.payload,\n }\n\n default:\n return state\n }\n}\n\nexport const initialWebSocketState: WebSocketState = {\n model: null,\n hasCompletedInitialSync: false,\n isSyncing: false,\n isConnected: false,\n isConnecting: false,\n connectionParams: null,\n websocketInstance: null,\n connectionAttemptId: null,\n connectionError: null,\n websocketError: null,\n}\n\n/**\n * Custom hook to manage a DataGrid WebSocket connection.\n * Handles:\n * - Fetching presigned URLs via a mutation hook\n * - Instantiating the DataGridWebSocket\n * - Connection status tracking\n * - Reconnection on disconnect\n * - Grid model updates and snapshot tracking\n */\nexport interface UseDataGridWebSocketOptions {\n onGridReady?: () => void\n onReplicaConnected?: () => void\n onReplicaDisconnected?: () => void\n}\n\nexport function useDataGridWebSocket(options?: UseDataGridWebSocketOptions) {\n const [state, dispatch] = useReducer(websocketReducer, initialWebSocketState)\n\n const connectionAttemptCounter = useRef(0)\n const activeConnectionAttemptIdRef = useRef<number | null>(null)\n\n const modelSnapshot = useCRDTModelView(state.model)\n\n const isDocumentVisible = useDocumentVisibility()\n\n const {\n mutateAsync: establishWebsocketConnection,\n isPending: isEstablishingWebsocketConnection,\n error: errorEstablishingWebsocketConnection,\n presignedUrl,\n reset: resetEstablishWebsocketConnection,\n clearPresignedUrl,\n } = useEstablishWebsocketConnection()\n\n // Update model creation handler - only set model, don't reset to null on disconnect\n const handleModelCreate = useCallback(\n (newModel: GridModel) => {\n dispatch({ type: 'MODEL_CREATED', payload: newModel })\n },\n [dispatch],\n )\n\n // Memoize websocket options to prevent unnecessary re-renders (excluding model to avoid circular deps)\n const websocketOptionsWithoutModel = useMemo(\n () => ({\n onGridReady: () => {\n dispatch({ type: 'GRID_READY' })\n options?.onGridReady?.()\n },\n onStatusChange: (open: boolean) =>\n dispatch({ type: open ? 'CONNECTION_OPENED' : 'CONNECTION_CLOSED' }),\n onModelCreate: handleModelCreate,\n onReplicaConnected: options?.onReplicaConnected,\n onReplicaDisconnected: options?.onReplicaDisconnected,\n onSyncStart: () => dispatch({ type: 'SYNC_STARTED' }),\n onSyncEnd: () => dispatch({ type: 'SYNC_ENDED' }),\n onError: (error: unknown) =>\n dispatch({ type: 'WEBSOCKET_ERROR', payload: error }),\n }),\n [\n handleModelCreate,\n options?.onGridReady,\n options?.onReplicaConnected,\n options?.onReplicaDisconnected,\n ],\n )\n\n // Initiate (or re-initiate) a connection\n const connect = useCallback(\n (replicaId: number, sessionId: string) => {\n resetEstablishWebsocketConnection()\n\n const isDifferentConnection =\n !state.connectionParams ||\n state.connectionParams.replicaId !== replicaId ||\n state.connectionParams.sessionId !== sessionId\n\n if (isDifferentConnection) {\n clearPresignedUrl()\n }\n\n connectionAttemptCounter.current += 1\n const attemptId = connectionAttemptCounter.current\n\n dispatch({\n type: 'CONNECT_REQUESTED',\n payload: { replicaId, sessionId, attemptId },\n })\n },\n [\n dispatch,\n state.connectionParams,\n resetEstablishWebsocketConnection,\n clearPresignedUrl,\n connectionAttemptCounter,\n ],\n )\n\n /**\n * Establish the WebSocket connection when conditions are met.\n * Uses the current model state at connection time for reconnections to the same session.\n * Note: Changes to `model` alone do NOT trigger reconnection - only changes to connection\n * params, visibility, or connection status will trigger a new connection attempt.\n */\n useEffect(() => {\n // Don't attempt connection if conditions aren't met\n const { connectionParams, connectionAttemptId } = state\n\n if (\n !connectionParams ||\n connectionAttemptId === null ||\n state.isConnected ||\n state.isConnecting ||\n isEstablishingWebsocketConnection ||\n errorEstablishingWebsocketConnection ||\n state.connectionError ||\n !isDocumentVisible\n ) {\n return\n }\n\n activeConnectionAttemptIdRef.current = connectionAttemptId\n\n // Use current model state for reconnections to same session\n // (model may have been created by a previous connection)\n establishWebsocketConnection({\n replicaId: connectionParams.replicaId,\n sessionId: connectionParams.sessionId,\n websocketOptions: {\n ...websocketOptionsWithoutModel,\n model: state.model, // Current model at connection time\n },\n })\n .then(ws => {\n if (activeConnectionAttemptIdRef.current !== connectionAttemptId) {\n ws.disconnect()\n return\n }\n\n dispatch({ type: 'CONNECTION_ESTABLISHED', payload: ws })\n })\n .catch(err => {\n dispatch({ type: 'CONNECTION_ERROR', payload: err })\n console.error('Failed to establish WebSocket', err)\n })\n }, [\n state,\n state.connectionParams,\n state.connectionAttemptId,\n state.isConnected,\n state.isConnecting,\n isEstablishingWebsocketConnection,\n errorEstablishingWebsocketConnection,\n isDocumentVisible,\n establishWebsocketConnection,\n websocketOptionsWithoutModel,\n ])\n\n const previousConnectionParams =\n useRef<WebSocketState['connectionParams']>(null)\n\n useEffect(() => {\n const previous = previousConnectionParams.current\n const current = state.connectionParams\n\n if (\n previous &&\n current &&\n (previous.replicaId !== current.replicaId ||\n previous.sessionId !== current.sessionId)\n ) {\n state.websocketInstance?.disconnect()\n }\n\n previousConnectionParams.current = current\n }, [state.connectionParams, state.websocketInstance])\n\n useEffect(() => {\n return () => {\n state.websocketInstance?.disconnect()\n activeConnectionAttemptIdRef.current = null\n }\n }, [state.websocketInstance])\n\n /**\n * Checks if the model snapshot contains the minimum data required for rendering (columns and rows).\n */\n function isModelRenderable(model: GridModel | null) {\n if (!model || !model.api.getSnapshot() || !modelSnapshot) {\n return false\n }\n const { columnNames, columnOrder, rows } = modelSnapshot\n const columnsReady = columnNames.length >= 1\n const orderReady = columnOrder.length >= 1\n const rowsReady = rows.length >= 0\n return columnsReady && orderReady && rowsReady\n }\n\n return {\n isConnected: state.isConnected,\n websocketInstance: state.websocketInstance,\n hasCompletedInitialSync: state.hasCompletedInitialSync,\n isSyncing: state.isSyncing,\n model: state.model,\n modelSnapshot,\n connect,\n presignedUrl,\n errorEstablishingWebsocketConnection:\n state.connectionError ?? errorEstablishingWebsocketConnection,\n websocketError: state.websocketError,\n hasSufficientData: isModelRenderable(state.model),\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useDataGridWebsocket.js","names":["GridModel","useCRDTModelView","normalizeWebsocketError","useCallback","useEffect","useMemo","useReducer","useRef","DataGridWebSocket","useEstablishWebsocketConnection","useDocumentVisibility","WebSocketState","model","hasCompletedInitialSync","isSyncing","isConnected","isConnecting","connectionParams","replicaId","sessionId","websocketInstance","connectionAttemptId","connectionError","websocketError","WebSocketAction","type","payload","attemptId","websocketReducer","state","action","isSameConnection","initialWebSocketState","UseDataGridWebSocketOptions","onGridReady","onReplicaConnected","onReplicaDisconnected","useDataGridWebSocket","options","dispatch","connectionAttemptCounter","activeConnectionAttemptIdRef","modelSnapshot","isDocumentVisible","mutateAsync","establishWebsocketConnection","isPending","isEstablishingWebsocketConnection","error","errorEstablishingWebsocketConnection","presignedUrl","reset","resetEstablishWebsocketConnection","clearPresignedUrl","handleModelCreate","newModel","websocketOptionsWithoutModel","onStatusChange","open","onModelCreate","onSyncStart","onSyncEnd","onError","connect","isDifferentConnection","current","websocketOptions","then","ws","disconnect","catch","err","console","previousConnectionParams","previous","isModelRenderable","api","getSnapshot","columnNames","columnOrder","rows","columnsReady","length","orderReady","rowsReady","hasSufficientData"],"sources":["../../../src/components/DataGrid/useDataGridWebsocket.ts"],"sourcesContent":["import { GridModel } from '@/components/DataGrid/DataGridTypes'\nimport { useCRDTModelView } from '@/components/DataGrid/useCRDTModelView'\nimport { normalizeWebsocketError } from '@/components/DataGrid/utils/normalizeWebsocketError'\nimport { useCallback, useEffect, useMemo, useReducer, useRef } from 'react'\nimport { DataGridWebSocket } from './DataGridWebSocket'\nimport { useEstablishWebsocketConnection } from '@/synapse-queries/grid/useEstablishWebsocketConnection'\nimport { useDocumentVisibility } from '@react-hookz/web'\n\n// State type\ninterface WebSocketState {\n model: GridModel | null\n /**\n * True if the WebSocket has finished the initial sync and can process CRDT updates.\n * Corresponds to the `GRID_READY` action.\n */\n hasCompletedInitialSync: boolean\n /**\n * True while a clock-sync exchange is in progress. Set on every outgoing\n * `synchronize-clock` and cleared on `ResponseComplete`, which the server\n * sends once per exchange to signal \"we're synchronized\" — not per request.\n */\n isSyncing: boolean\n isConnected: boolean\n isConnecting: boolean\n connectionParams: {\n replicaId: number\n sessionId: string\n } | null\n websocketInstance: DataGridWebSocket | null\n connectionAttemptId: number | null\n connectionError: unknown\n websocketError: unknown\n}\n\n// Action types\ntype WebSocketAction =\n | {\n type: 'CONNECT_REQUESTED'\n payload: { replicaId: number; sessionId: string; attemptId: number }\n }\n | { type: 'CONNECTION_ESTABLISHED'; payload: DataGridWebSocket }\n | { type: 'CONNECTION_OPENED' }\n | { type: 'CONNECTION_CLOSED' }\n | { type: 'GRID_READY' }\n | { type: 'SYNC_STARTED' }\n | { type: 'SYNC_ENDED' }\n | { type: 'MODEL_CREATED'; payload: GridModel }\n | { type: 'CONNECTION_ERROR'; payload: unknown }\n | { type: 'WEBSOCKET_ERROR'; payload: unknown }\n\n// Reducer function\nfunction websocketReducer(\n state: WebSocketState,\n action: WebSocketAction,\n): WebSocketState {\n switch (action.type) {\n case 'CONNECT_REQUESTED': {\n const isSameConnection =\n state.connectionParams &&\n state.connectionParams.replicaId === action.payload.replicaId &&\n state.connectionParams.sessionId === action.payload.sessionId\n\n return {\n ...state,\n connectionParams: {\n replicaId: action.payload.replicaId,\n sessionId: action.payload.sessionId,\n },\n hasCompletedInitialSync: isSameConnection\n ? state.hasCompletedInitialSync\n : false,\n isSyncing: false,\n model: isSameConnection ? state.model : null,\n isConnected: false,\n isConnecting: false,\n connectionAttemptId: action.payload.attemptId,\n connectionError: null,\n }\n }\n\n case 'CONNECTION_ESTABLISHED':\n return {\n ...state,\n websocketInstance: action.payload,\n isConnecting: true,\n }\n\n case 'CONNECTION_OPENED':\n return {\n ...state,\n isConnected: true,\n isConnecting: false,\n }\n\n case 'CONNECTION_CLOSED':\n return {\n ...state,\n isConnected: false,\n isConnecting: false,\n isSyncing: false,\n }\n\n case 'GRID_READY':\n return {\n ...state,\n hasCompletedInitialSync: true,\n }\n\n case 'SYNC_STARTED':\n return {\n ...state,\n isSyncing: true,\n }\n\n case 'SYNC_ENDED':\n return {\n ...state,\n isSyncing: false,\n }\n\n case 'MODEL_CREATED':\n return {\n ...state,\n model: action.payload,\n }\n\n case 'CONNECTION_ERROR':\n return {\n ...state,\n connectionError: action.payload,\n isConnecting: false,\n }\n\n case 'WEBSOCKET_ERROR':\n return {\n ...state,\n websocketError: action.payload,\n }\n\n default:\n return state\n }\n}\n\nexport const initialWebSocketState: WebSocketState = {\n model: null,\n hasCompletedInitialSync: false,\n isSyncing: false,\n isConnected: false,\n isConnecting: false,\n connectionParams: null,\n websocketInstance: null,\n connectionAttemptId: null,\n connectionError: null,\n websocketError: null,\n}\n\n/**\n * Custom hook to manage a DataGrid WebSocket connection.\n * Handles:\n * - Fetching presigned URLs via a mutation hook\n * - Instantiating the DataGridWebSocket\n * - Connection status tracking\n * - Reconnection on disconnect\n * - Grid model updates and snapshot tracking\n */\nexport interface UseDataGridWebSocketOptions {\n onGridReady?: () => void\n onReplicaConnected?: () => void\n onReplicaDisconnected?: () => void\n}\n\nexport function useDataGridWebSocket(options?: UseDataGridWebSocketOptions) {\n const [state, dispatch] = useReducer(websocketReducer, initialWebSocketState)\n\n const connectionAttemptCounter = useRef(0)\n const activeConnectionAttemptIdRef = useRef<number | null>(null)\n\n const modelSnapshot = useCRDTModelView(state.model)\n\n const isDocumentVisible = useDocumentVisibility()\n\n const {\n mutateAsync: establishWebsocketConnection,\n isPending: isEstablishingWebsocketConnection,\n error: errorEstablishingWebsocketConnection,\n presignedUrl,\n reset: resetEstablishWebsocketConnection,\n clearPresignedUrl,\n } = useEstablishWebsocketConnection()\n\n // Update model creation handler - only set model, don't reset to null on disconnect\n const handleModelCreate = useCallback(\n (newModel: GridModel) => {\n dispatch({ type: 'MODEL_CREATED', payload: newModel })\n },\n [dispatch],\n )\n\n // Memoize websocket options to prevent unnecessary re-renders (excluding model to avoid circular deps)\n const websocketOptionsWithoutModel = useMemo(\n () => ({\n onGridReady: () => {\n dispatch({ type: 'GRID_READY' })\n options?.onGridReady?.()\n },\n onStatusChange: (open: boolean) =>\n dispatch({ type: open ? 'CONNECTION_OPENED' : 'CONNECTION_CLOSED' }),\n onModelCreate: handleModelCreate,\n onReplicaConnected: options?.onReplicaConnected,\n onReplicaDisconnected: options?.onReplicaDisconnected,\n onSyncStart: () => dispatch({ type: 'SYNC_STARTED' }),\n onSyncEnd: () => dispatch({ type: 'SYNC_ENDED' }),\n onError: (error: unknown) =>\n dispatch({\n type: 'WEBSOCKET_ERROR',\n payload: normalizeWebsocketError(error),\n }),\n }),\n [\n handleModelCreate,\n options?.onGridReady,\n options?.onReplicaConnected,\n options?.onReplicaDisconnected,\n ],\n )\n\n // Initiate (or re-initiate) a connection\n const connect = useCallback(\n (replicaId: number, sessionId: string) => {\n resetEstablishWebsocketConnection()\n\n const isDifferentConnection =\n !state.connectionParams ||\n state.connectionParams.replicaId !== replicaId ||\n state.connectionParams.sessionId !== sessionId\n\n if (isDifferentConnection) {\n clearPresignedUrl()\n }\n\n connectionAttemptCounter.current += 1\n const attemptId = connectionAttemptCounter.current\n\n dispatch({\n type: 'CONNECT_REQUESTED',\n payload: { replicaId, sessionId, attemptId },\n })\n },\n [\n dispatch,\n state.connectionParams,\n resetEstablishWebsocketConnection,\n clearPresignedUrl,\n connectionAttemptCounter,\n ],\n )\n\n /**\n * Establish the WebSocket connection when conditions are met.\n * Uses the current model state at connection time for reconnections to the same session.\n * Note: Changes to `model` alone do NOT trigger reconnection - only changes to connection\n * params, visibility, or connection status will trigger a new connection attempt.\n */\n useEffect(() => {\n // Don't attempt connection if conditions aren't met\n const { connectionParams, connectionAttemptId } = state\n\n if (\n !connectionParams ||\n connectionAttemptId === null ||\n state.isConnected ||\n state.isConnecting ||\n isEstablishingWebsocketConnection ||\n errorEstablishingWebsocketConnection ||\n state.connectionError ||\n !isDocumentVisible\n ) {\n return\n }\n\n activeConnectionAttemptIdRef.current = connectionAttemptId\n\n // Use current model state for reconnections to same session\n // (model may have been created by a previous connection)\n establishWebsocketConnection({\n replicaId: connectionParams.replicaId,\n sessionId: connectionParams.sessionId,\n websocketOptions: {\n ...websocketOptionsWithoutModel,\n model: state.model, // Current model at connection time\n },\n })\n .then(ws => {\n if (activeConnectionAttemptIdRef.current !== connectionAttemptId) {\n ws.disconnect()\n return\n }\n\n dispatch({ type: 'CONNECTION_ESTABLISHED', payload: ws })\n })\n .catch(err => {\n dispatch({ type: 'CONNECTION_ERROR', payload: err })\n console.error('Failed to establish WebSocket', err)\n })\n }, [\n state,\n state.connectionParams,\n state.connectionAttemptId,\n state.isConnected,\n state.isConnecting,\n isEstablishingWebsocketConnection,\n errorEstablishingWebsocketConnection,\n isDocumentVisible,\n establishWebsocketConnection,\n websocketOptionsWithoutModel,\n ])\n\n const previousConnectionParams =\n useRef<WebSocketState['connectionParams']>(null)\n\n useEffect(() => {\n const previous = previousConnectionParams.current\n const current = state.connectionParams\n\n if (\n previous &&\n current &&\n (previous.replicaId !== current.replicaId ||\n previous.sessionId !== current.sessionId)\n ) {\n state.websocketInstance?.disconnect()\n }\n\n previousConnectionParams.current = current\n }, [state.connectionParams, state.websocketInstance])\n\n useEffect(() => {\n return () => {\n state.websocketInstance?.disconnect()\n activeConnectionAttemptIdRef.current = null\n }\n }, [state.websocketInstance])\n\n /**\n * Checks if the model snapshot contains the minimum data required for rendering (columns and rows).\n */\n function isModelRenderable(model: GridModel | null) {\n if (!model || !model.api.getSnapshot() || !modelSnapshot) {\n return false\n }\n const { columnNames, columnOrder, rows } = modelSnapshot\n const columnsReady = columnNames.length >= 1\n const orderReady = columnOrder.length >= 1\n const rowsReady = rows.length >= 0\n return columnsReady && orderReady && rowsReady\n }\n\n return {\n isConnected: state.isConnected,\n websocketInstance: state.websocketInstance,\n hasCompletedInitialSync: state.hasCompletedInitialSync,\n isSyncing: state.isSyncing,\n model: state.model,\n modelSnapshot,\n connect,\n presignedUrl,\n errorEstablishingWebsocketConnection:\n state.connectionError ?? errorEstablishingWebsocketConnection,\n websocketError: state.websocketError,\n hasSufficientData: isModelRenderable(state.model),\n }\n}\n"],"mappings":";;;;;;AAmDA,SAAS4B,EACPC,GACAC,GACgB;CAChB,QAAQA,EAAOL,MAAf;EACE,KAAK,qBAAqB;GACxB,IAAMM,IACJF,EAAMZ,oBACNY,EAAMZ,iBAAiBC,cAAcY,EAAOJ,QAAQR,aACpDW,EAAMZ,iBAAiBE,cAAcW,EAAOJ,QAAQP;GAEtD,OAAO;IACL,GAAGU;IACHZ,kBAAkB;KAChBC,WAAWY,EAAOJ,QAAQR;KAC1BC,WAAWW,EAAOJ,QAAQP;IAC5B;IACAN,yBAAyBkB,IACrBF,EAAMhB,0BACN;IACJC,WAAW;IACXF,OAAOmB,IAAmBF,EAAMjB,QAAQ;IACxCG,aAAa;IACbC,cAAc;IACdK,qBAAqBS,EAAOJ,QAAQC;IACpCL,iBAAiB;GACnB;EACF;EAEA,KAAK,0BACH,OAAO;GACL,GAAGO;GACHT,mBAAmBU,EAAOJ;GAC1BV,cAAc;EAChB;EAEF,KAAK,qBACH,OAAO;GACL,GAAGa;GACHd,aAAa;GACbC,cAAc;EAChB;EAEF,KAAK,qBACH,OAAO;GACL,GAAGa;GACHd,aAAa;GACbC,cAAc;GACdF,WAAW;EACb;EAEF,KAAK,cACH,OAAO;GACL,GAAGe;GACHhB,yBAAyB;EAC3B;EAEF,KAAK,gBACH,OAAO;GACL,GAAGgB;GACHf,WAAW;EACb;EAEF,KAAK,cACH,OAAO;GACL,GAAGe;GACHf,WAAW;EACb;EAEF,KAAK,iBACH,OAAO;GACL,GAAGe;GACHjB,OAAOkB,EAAOJ;EAChB;EAEF,KAAK,oBACH,OAAO;GACL,GAAGG;GACHP,iBAAiBQ,EAAOJ;GACxBV,cAAc;EAChB;EAEF,KAAK,mBACH,OAAO;GACL,GAAGa;GACHN,gBAAgBO,EAAOJ;EACzB;EAEF,SACE,OAAOG;CACX;AACF;AAEA,IAAaG,IAAwC;CACnDpB,OAAO;CACPC,yBAAyB;CACzBC,WAAW;CACXC,aAAa;CACbC,cAAc;CACdC,kBAAkB;CAClBG,mBAAmB;CACnBC,qBAAqB;CACrBC,iBAAiB;CACjBC,gBAAgB;AAClB;AAiBA,SAAgBc,EAAqBC,GAAuC;CAC1E,IAAM,CAACT,GAAOU,KAAYjC,EAAWsB,GAAkBI,CAAqB,GAEtEQ,IAA2BjC,EAAO,CAAC,GACnCkC,IAA+BlC,EAAsB,IAAI,GAEzDmC,IAAgBzC,EAAiB4B,EAAMjB,KAAK,GAE5C+B,IAAoBjC,EAAsB,GAE1C,EACJkC,aAAaC,GACbC,WAAWC,GACXC,OAAOC,GACPC,iBACAC,OAAOC,GACPC,yBACE5C,EAAgC,GAG9B6C,IAAoBnD,GACvBoD,MAAwB;EACvBhB,EAAS;GAAEd,MAAM;GAAiBC,SAAS6B;EAAS,CAAC;CACvD,GACA,CAAChB,CAAQ,CACX,GAGMiB,IAA+BnD,SAC5B;EACL6B,mBAAmB;GAEjBI,AADAC,EAAS,EAAEd,MAAM,aAAa,CAAC,GAC/Ba,GAASJ,cAAc;EACzB;EACAuB,iBAAiBC,MACfnB,EAAS,EAAEd,MAAMiC,IAAO,sBAAsB,oBAAoB,CAAC;EACrEC,eAAeL;EACfnB,oBAAoBG,GAASH;EAC7BC,uBAAuBE,GAASF;EAChCwB,mBAAmBrB,EAAS,EAAEd,MAAM,eAAe,CAAC;EACpDoC,iBAAiBtB,EAAS,EAAEd,MAAM,aAAa,CAAC;EAChDqC,UAAUd,MACRT,EAAS;GACPd,MAAM;GACNC,SAASxB,EAAwB8C,CAAK;EACxC,CAAC;CACL,IACA;EACEM;EACAhB,GAASJ;EACTI,GAASH;EACTG,GAASF;CAAqB,CAElC,GAGM2B,IAAU5D,GACbe,GAAmBC,MAAsB;EAYxCqB,AAXAY,EAAkC,IAGhC,CAACvB,EAAMZ,oBACPY,EAAMZ,iBAAiBC,cAAcA,KACrCW,EAAMZ,iBAAiBE,cAAcA,MAGrCkC,EAAkB,GAGpBb,EAAyByB,WAAW;EACpC,IAAMtC,IAAYa,EAAyByB;EAE3C1B,EAAS;GACPd,MAAM;GACNC,SAAS;IAAER;IAAWC;IAAWQ;GAAU;EAC7C,CAAC;CACH,GACA;EACEY;EACAV,EAAMZ;EACNmC;EACAC;EACAb;CAAwB,CAE5B;CAQApC,QAAgB;EAEd,IAAM,EAAEa,qBAAkBI,2BAAwBQ;EAGhD,CAACZ,KACDI,MAAwB,QACxBQ,EAAMd,eACNc,EAAMb,gBACN+B,KACAE,KACApB,EAAMP,mBACN,CAACqB,MAKHF,EAA6BwB,UAAU5C,GAIvCwB,EAA6B;GAC3B3B,WAAWD,EAAiBC;GAC5BC,WAAWF,EAAiBE;GAC5B+C,kBAAkB;IAChB,GAAGV;IACH5C,OAAOiB,EAAMjB;GACf;EACF,CAAC,EACEuD,MAAKC,MAAM;GACV,IAAI3B,EAA6BwB,YAAY5C,GAAqB;IAChE+C,EAAGC,WAAW;IACd;GACF;GAEA9B,EAAS;IAAEd,MAAM;IAA0BC,SAAS0C;GAAG,CAAC;EAC1D,CAAC,EACAE,OAAMC,MAAO;GAEZC,AADAjC,EAAS;IAAEd,MAAM;IAAoBC,SAAS6C;GAAI,CAAC,GACnDC,QAAQxB,MAAM,iCAAiCuB,CAAG;EACpD,CAAC;CACL,GAAG;EACD1C;EACAA,EAAMZ;EACNY,EAAMR;EACNQ,EAAMd;EACNc,EAAMb;EACN+B;EACAE;EACAN;EACAE;EACAW;CAA4B,CAC7B;CAED,IAAMiB,IACJlE,EAA2C,IAAI;CAkBjDH,AAhBAA,QAAgB;EACd,IAAMsE,IAAWD,EAAyBR,SACpCA,IAAUpC,EAAMZ;EAWtBwD,AAREC,KACAT,MACCS,EAASxD,cAAc+C,EAAQ/C,aAC9BwD,EAASvD,cAAc8C,EAAQ9C,cAEjCU,EAAMT,mBAAmBiD,WAAW,GAGtCI,EAAyBR,UAAUA;CACrC,GAAG,CAACpC,EAAMZ,kBAAkBY,EAAMT,iBAAiB,CAAC,GAEpDhB,cACe;EAEXqC,AADAZ,EAAMT,mBAAmBiD,WAAW,GACpC5B,EAA6BwB,UAAU;CACzC,GACC,CAACpC,EAAMT,iBAAiB,CAAC;CAK5B,SAASuD,EAAkB/D,GAAyB;EAClD,IAAI,CAACA,KAAS,CAACA,EAAMgE,IAAIC,YAAY,KAAK,CAACnC,GACzC,OAAO;EAET,IAAM,EAAEoC,gBAAaC,gBAAaC,YAAStC,GACrCuC,IAAeH,EAAYI,UAAU,GACrCC,IAAaJ,EAAYG,UAAU,GACnCE,IAAYJ,EAAKE,UAAU;EACjC,OAAOD,KAAgBE,KAAcC;CACvC;CAEA,OAAO;EACLrE,aAAac,EAAMd;EACnBK,mBAAmBS,EAAMT;EACzBP,yBAAyBgB,EAAMhB;EAC/BC,WAAWe,EAAMf;EACjBF,OAAOiB,EAAMjB;EACb8B;EACAqB;EACAb;EACAD,sCACEpB,EAAMP,mBAAmB2B;EAC3B1B,gBAAgBM,EAAMN;EACtB8D,mBAAmBV,EAAkB9C,EAAMjB,KAAK;CAClD;AACF"}
|
|
@@ -13,6 +13,7 @@ type ColumnConfig = {
|
|
|
13
13
|
showPinIcon?: boolean;
|
|
14
14
|
isPinned?: boolean;
|
|
15
15
|
onTogglePin?: () => void;
|
|
16
|
+
isUpsertKey?: boolean;
|
|
16
17
|
/**
|
|
17
18
|
* Optional column-level schema info. When provided, paste behavior coerces
|
|
18
19
|
* empty pasted cells to the schema-correct blank (undefined for optional
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"columnFactory.d.ts","sourceRoot":"","sources":["../../../../src/components/DataGrid/utils/columnFactory.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAA;AAC7E,OAAO,EAGL,MAAM,EAIP,MAAM,wCAAwC,CAAA;AAuE/C,KAAK,YAAY,GAAG;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,gBAAgB,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAAA;IACrD,UAAU,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;CACxC,CAAA;
|
|
1
|
+
{"version":3,"file":"columnFactory.d.ts","sourceRoot":"","sources":["../../../../src/components/DataGrid/utils/columnFactory.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAA;AAC7E,OAAO,EAGL,MAAM,EAIP,MAAM,wCAAwC,CAAA;AAuE/C,KAAK,YAAY,GAAG;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,gBAAgB,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAAA;IACrD,UAAU,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;CACxC,CAAA;AAgMD,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAGzD"}
|
|
@@ -53,8 +53,8 @@ function g(e, t) {
|
|
|
53
53
|
}
|
|
54
54
|
return n.displayName = `CellWithTooltip(${t})`, n;
|
|
55
55
|
}
|
|
56
|
-
function _(e) {
|
|
57
|
-
return e ? "header-cell-required" : "header-cell";
|
|
56
|
+
function _(e, t) {
|
|
57
|
+
return t ? "header-upsert-key" : e ? "header-cell-required" : "header-cell";
|
|
58
58
|
}
|
|
59
59
|
function v(e, t) {
|
|
60
60
|
return ({ rowData: n }) => ({
|
|
@@ -80,11 +80,12 @@ function b(e, t) {
|
|
|
80
80
|
name: e.columnName,
|
|
81
81
|
description: e.description,
|
|
82
82
|
isRequired: e.isRequired,
|
|
83
|
+
isUpsertKey: e.isUpsertKey,
|
|
83
84
|
showPinIcon: e.showPinIcon,
|
|
84
85
|
isPinned: e.isPinned,
|
|
85
86
|
onTogglePin: e.onTogglePin
|
|
86
87
|
}),
|
|
87
|
-
headerClassName: _(e.isRequired),
|
|
88
|
+
headerClassName: _(e.isRequired, !!e.isUpsertKey),
|
|
88
89
|
minWidth: a,
|
|
89
90
|
basis: a,
|
|
90
91
|
grow: 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"columnFactory.js","names":["dateTimeColumn","EnumeratedValue","FlatTypeInfo","SchemaPropertyInfo","CellComponent","CellProps","Column","createTextColumn","floatColumn","keyColumn","autocompleteColumn","autocompleteMultipleEnumColumn","calculateDefaultColumnWidth","HeaderOptions","ColumnHeaderWithTooltip","Tooltip","SmartToyTwoTone","DataGridRow","wrapPasteValueWithSchemaCoercion","getEmptyValue","withChangeIndicatorTooltip","OriginalComponent","T","C","colName","CellWithTooltip","props","$","_c","changeInfo","rowData","__cellChangeInfo","isAgent","category","t0","t1","tooltipText","position","top","right","width","height","zIndex","pointerEvents","t2","focus","fontSize","alignSelf","flexShrink","mr","color","backgroundColor","t3","displayName","ColumnConfig","columnName","description","disabled","typeInfo","enumeratedValues","isRequired","customWidth","showPinIcon","isPinned","onTogglePin","schemaPropertyInfo","getHeaderClassName","createDeleteValue","Record","createParseUserInput","value","trimmedValue","trim","createBaseColumn","config","columnImpl","headerOptions","hasDescription","undefined","keyed","component","title","headerClassName","minWidth","basis","grow","shrink","deleteValue","pasteValue","stickyLeft","COLUMN_FACTORIES","multipleEnum","choices","colType","type","limitTags","clearValue","boolean","number","enumerated","date-time","text","continuousUpdates","deletedValue","parseUserInput","cellClassName","getColumnType","length","format","isArray","createColumn","columnType"],"sources":["../../../../src/components/DataGrid/utils/columnFactory.tsx"],"sourcesContent":["import { dateTimeColumn } from '@/components/DataGrid/columns/DateTimeColumn'\nimport { EnumeratedValue } from '@/utils/jsonschema/getEnumeratedValues'\nimport { FlatTypeInfo } from '@/utils/jsonschema/getType'\nimport { SchemaPropertyInfo } from '@/utils/jsonschema/getSchemaPropertyInfo'\nimport {\n CellComponent,\n CellProps,\n Column,\n createTextColumn,\n floatColumn,\n keyColumn,\n} from '@sage-bionetworks/react-datasheet-grid'\nimport { autocompleteColumn } from '../columns/AutocompleteColumn'\nimport { autocompleteMultipleEnumColumn } from '../columns/AutocompleteMultipleEnumColumn'\nimport {\n calculateDefaultColumnWidth,\n HeaderOptions,\n} from './calculateColumnWidth'\nimport { ColumnHeaderWithTooltip } from '../components/ColumnHeaderWithTooltip'\nimport { Tooltip } from '@mui/material'\nimport { SmartToyTwoTone } from '@mui/icons-material'\nimport type { DataGridRow } from '../DataGridTypes'\nimport { wrapPasteValueWithSchemaCoercion } from './schemaAwarePasteValue'\nimport { getEmptyValue } from './getEmptyValue'\n\n/**\n * Wraps a column cell component to overlay change-attribution indicators:\n * - Non-agent changes: invisible 7×7px tooltip trigger over the CSS triangle (top-right).\n * - Agent changes: robot icon (1em, vertically centered on the right) with no triangle.\n */\nfunction withChangeIndicatorTooltip<T, C>(\n OriginalComponent: CellComponent<T, C>,\n colName: string,\n): CellComponent<T, C> {\n function CellWithTooltip(props: CellProps<T, C>) {\n const changeInfo = (props.rowData as DataGridRow).__cellChangeInfo?.[\n colName\n ]\n const isAgent =\n changeInfo?.category === 'own-agent' ||\n changeInfo?.category === 'other-agent'\n return (\n <>\n <OriginalComponent {...props} />\n {changeInfo && !isAgent && (\n <Tooltip title={changeInfo.tooltipText} placement=\"top-end\">\n <div\n style={{\n position: 'absolute',\n top: 0,\n right: 0,\n width: 7,\n height: 7,\n zIndex: 21,\n pointerEvents: 'auto',\n }}\n />\n </Tooltip>\n )}\n {isAgent && !props.focus && (\n <Tooltip title={changeInfo.tooltipText} placement=\"left\">\n <SmartToyTwoTone\n sx={{\n fontSize: '1em',\n alignSelf: 'center',\n flexShrink: 0,\n mr: 0.5,\n zIndex: 21,\n pointerEvents: 'auto',\n color: 'grey.400',\n backgroundColor: 'transparent',\n }}\n />\n </Tooltip>\n )}\n </>\n )\n }\n CellWithTooltip.displayName = `CellWithTooltip(${colName})`\n return CellWithTooltip\n}\n\ntype ColumnConfig = {\n columnName: string\n description?: string\n disabled?: boolean\n typeInfo: FlatTypeInfo | null\n enumeratedValues: EnumeratedValue[] | string[] | null\n isRequired: boolean\n customWidth?: number\n showPinIcon?: boolean\n isPinned?: boolean\n onTogglePin?: () => void\n /**\n * Optional column-level schema info. When provided, paste behavior coerces\n * empty pasted cells to the schema-correct blank (undefined for optional\n * columns, null for required columns). Omit to preserve the column impl's\n * default paste behavior.\n */\n schemaPropertyInfo?: SchemaPropertyInfo\n}\n\nfunction getHeaderClassName(isRequired: boolean): string {\n return isRequired ? 'header-cell-required' : 'header-cell'\n}\n\nfunction createDeleteValue(columnName: string, isRequired?: boolean) {\n return ({ rowData }: { rowData: Record<string, unknown> }) => ({\n ...rowData,\n [columnName]: getEmptyValue(isRequired),\n })\n}\n\nfunction createParseUserInput(isRequired?: boolean) {\n return (value: string) => {\n const trimmedValue = value.trim()\n if (trimmedValue === '') {\n return getEmptyValue(isRequired)\n }\n return trimmedValue\n }\n}\n\nfunction createBaseColumn(config: ColumnConfig, columnImpl: any) {\n const headerOptions: HeaderOptions = {\n showPinIcon: config.showPinIcon,\n hasDescription: !!config.description,\n }\n\n const width =\n config.customWidth ??\n calculateDefaultColumnWidth(\n config.columnName,\n undefined, // propertyInfo is not available here, passed from higher level\n headerOptions,\n )\n\n const keyed = keyColumn(config.columnName, columnImpl)\n return {\n ...keyed,\n component: withChangeIndicatorTooltip(keyed.component!, config.columnName),\n title: (\n <ColumnHeaderWithTooltip\n name={config.columnName}\n description={config.description}\n isRequired={config.isRequired}\n showPinIcon={config.showPinIcon}\n isPinned={config.isPinned}\n onTogglePin={config.onTogglePin}\n />\n ),\n headerClassName: getHeaderClassName(config.isRequired),\n minWidth: width,\n basis: width,\n grow: 0,\n shrink: 0,\n disabled: config.disabled,\n deleteValue: createDeleteValue(config.columnName, config.isRequired),\n pasteValue: wrapPasteValueWithSchemaCoercion(\n keyed.pasteValue,\n config.columnName,\n config.schemaPropertyInfo,\n ),\n stickyLeft: config.isPinned,\n }\n}\n\nconst COLUMN_FACTORIES = {\n multipleEnum: (config: ColumnConfig) => {\n return createBaseColumn(\n config,\n autocompleteMultipleEnumColumn({\n choices: config.enumeratedValues ?? [],\n colType: config.typeInfo?.type || null,\n limitTags: 3,\n clearValue: getEmptyValue(config.isRequired),\n }),\n )\n },\n\n boolean: (config: ColumnConfig) => {\n return createBaseColumn(\n config,\n autocompleteColumn({\n choices: [true, false],\n colType: 'boolean',\n clearValue: getEmptyValue(config.isRequired),\n }),\n )\n },\n\n number: (config: ColumnConfig) => {\n return createBaseColumn(config, floatColumn)\n },\n\n enumerated: (config: ColumnConfig) => {\n return createBaseColumn(\n config,\n autocompleteColumn({\n choices: config.enumeratedValues ?? [],\n colType: config.typeInfo?.type || null,\n clearValue: getEmptyValue(config.isRequired),\n }),\n )\n },\n\n 'date-time': (config: ColumnConfig) => {\n const columnImpl = dateTimeColumn({\n colType: config.typeInfo?.type || null,\n })\n\n // Date-time needs special width calculation\n const headerOptions: HeaderOptions = {\n showPinIcon: config.showPinIcon,\n hasDescription: !!config.description,\n }\n\n const width =\n config.customWidth ??\n calculateDefaultColumnWidth(\n config.columnName,\n {\n type: config.typeInfo ?? undefined,\n isRequired: config.isRequired,\n enumeratedValues: null,\n },\n headerOptions,\n )\n\n return {\n ...createBaseColumn(config, columnImpl),\n minWidth: width,\n basis: width,\n }\n },\n\n text: (config: ColumnConfig) => {\n const columnImpl = createTextColumn({\n continuousUpdates: false,\n deletedValue: undefined,\n parseUserInput: createParseUserInput(config.isRequired),\n })\n\n return {\n ...createBaseColumn(config, columnImpl),\n cellClassName: 'MuiInputBase-input',\n }\n },\n}\n\nfunction getColumnType(\n typeInfo: FlatTypeInfo | null,\n enumeratedValues?: EnumeratedValue[] | string[] | null,\n): keyof typeof COLUMN_FACTORIES {\n if (!typeInfo) {\n return enumeratedValues && enumeratedValues.length > 0\n ? 'enumerated'\n : 'text'\n }\n\n if (typeInfo.format === 'date-time') {\n return 'date-time'\n }\n\n // Handle arrays - check if it's an array of enums\n if (typeInfo.isArray) {\n return 'multipleEnum'\n }\n\n if (typeInfo.type === 'boolean') {\n return 'boolean'\n }\n\n if (typeInfo.type === 'number' || typeInfo.type === 'integer') {\n return 'number'\n }\n\n if (enumeratedValues && enumeratedValues.length > 0) {\n return 'enumerated'\n }\n\n return 'text'\n}\n\nexport function createColumn(config: ColumnConfig): Column {\n const columnType = getColumnType(config.typeInfo, config.enumeratedValues)\n return COLUMN_FACTORIES[columnType](config)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"columnFactory.js","names":["dateTimeColumn","EnumeratedValue","FlatTypeInfo","SchemaPropertyInfo","CellComponent","CellProps","Column","createTextColumn","floatColumn","keyColumn","autocompleteColumn","autocompleteMultipleEnumColumn","calculateDefaultColumnWidth","HeaderOptions","ColumnHeaderWithTooltip","Tooltip","SmartToyTwoTone","DataGridRow","wrapPasteValueWithSchemaCoercion","getEmptyValue","withChangeIndicatorTooltip","OriginalComponent","T","C","colName","CellWithTooltip","props","$","_c","changeInfo","rowData","__cellChangeInfo","isAgent","category","t0","t1","tooltipText","position","top","right","width","height","zIndex","pointerEvents","t2","focus","fontSize","alignSelf","flexShrink","mr","color","backgroundColor","t3","displayName","ColumnConfig","columnName","description","disabled","typeInfo","enumeratedValues","isRequired","customWidth","showPinIcon","isPinned","onTogglePin","isUpsertKey","schemaPropertyInfo","getHeaderClassName","createDeleteValue","Record","createParseUserInput","value","trimmedValue","trim","createBaseColumn","config","columnImpl","headerOptions","hasDescription","undefined","keyed","component","title","headerClassName","minWidth","basis","grow","shrink","deleteValue","pasteValue","stickyLeft","COLUMN_FACTORIES","multipleEnum","choices","colType","type","limitTags","clearValue","boolean","number","enumerated","date-time","text","continuousUpdates","deletedValue","parseUserInput","cellClassName","getColumnType","length","format","isArray","createColumn","columnType"],"sources":["../../../../src/components/DataGrid/utils/columnFactory.tsx"],"sourcesContent":["import { dateTimeColumn } from '@/components/DataGrid/columns/DateTimeColumn'\nimport { EnumeratedValue } from '@/utils/jsonschema/getEnumeratedValues'\nimport { FlatTypeInfo } from '@/utils/jsonschema/getType'\nimport { SchemaPropertyInfo } from '@/utils/jsonschema/getSchemaPropertyInfo'\nimport {\n CellComponent,\n CellProps,\n Column,\n createTextColumn,\n floatColumn,\n keyColumn,\n} from '@sage-bionetworks/react-datasheet-grid'\nimport { autocompleteColumn } from '../columns/AutocompleteColumn'\nimport { autocompleteMultipleEnumColumn } from '../columns/AutocompleteMultipleEnumColumn'\nimport {\n calculateDefaultColumnWidth,\n HeaderOptions,\n} from './calculateColumnWidth'\nimport { ColumnHeaderWithTooltip } from '../components/ColumnHeaderWithTooltip'\nimport { Tooltip } from '@mui/material'\nimport { SmartToyTwoTone } from '@mui/icons-material'\nimport type { DataGridRow } from '../DataGridTypes'\nimport { wrapPasteValueWithSchemaCoercion } from './schemaAwarePasteValue'\nimport { getEmptyValue } from './getEmptyValue'\n\n/**\n * Wraps a column cell component to overlay change-attribution indicators:\n * - Non-agent changes: invisible 7×7px tooltip trigger over the CSS triangle (top-right).\n * - Agent changes: robot icon (1em, vertically centered on the right) with no triangle.\n */\nfunction withChangeIndicatorTooltip<T, C>(\n OriginalComponent: CellComponent<T, C>,\n colName: string,\n): CellComponent<T, C> {\n function CellWithTooltip(props: CellProps<T, C>) {\n const changeInfo = (props.rowData as DataGridRow).__cellChangeInfo?.[\n colName\n ]\n const isAgent =\n changeInfo?.category === 'own-agent' ||\n changeInfo?.category === 'other-agent'\n return (\n <>\n <OriginalComponent {...props} />\n {changeInfo && !isAgent && (\n <Tooltip title={changeInfo.tooltipText} placement=\"top-end\">\n <div\n style={{\n position: 'absolute',\n top: 0,\n right: 0,\n width: 7,\n height: 7,\n zIndex: 21,\n pointerEvents: 'auto',\n }}\n />\n </Tooltip>\n )}\n {isAgent && !props.focus && (\n <Tooltip title={changeInfo.tooltipText} placement=\"left\">\n <SmartToyTwoTone\n sx={{\n fontSize: '1em',\n alignSelf: 'center',\n flexShrink: 0,\n mr: 0.5,\n zIndex: 21,\n pointerEvents: 'auto',\n color: 'grey.400',\n backgroundColor: 'transparent',\n }}\n />\n </Tooltip>\n )}\n </>\n )\n }\n CellWithTooltip.displayName = `CellWithTooltip(${colName})`\n return CellWithTooltip\n}\n\ntype ColumnConfig = {\n columnName: string\n description?: string\n disabled?: boolean\n typeInfo: FlatTypeInfo | null\n enumeratedValues: EnumeratedValue[] | string[] | null\n isRequired: boolean\n customWidth?: number\n showPinIcon?: boolean\n isPinned?: boolean\n onTogglePin?: () => void\n isUpsertKey?: boolean\n /**\n * Optional column-level schema info. When provided, paste behavior coerces\n * empty pasted cells to the schema-correct blank (undefined for optional\n * columns, null for required columns). Omit to preserve the column impl's\n * default paste behavior.\n */\n schemaPropertyInfo?: SchemaPropertyInfo\n}\n\nfunction getHeaderClassName(isRequired: boolean, isUpsertKey: boolean): string {\n return isUpsertKey\n ? 'header-upsert-key'\n : isRequired\n ? 'header-cell-required'\n : 'header-cell'\n}\n\nfunction createDeleteValue(columnName: string, isRequired?: boolean) {\n return ({ rowData }: { rowData: Record<string, unknown> }) => ({\n ...rowData,\n [columnName]: getEmptyValue(isRequired),\n })\n}\n\nfunction createParseUserInput(isRequired?: boolean) {\n return (value: string) => {\n const trimmedValue = value.trim()\n if (trimmedValue === '') {\n return getEmptyValue(isRequired)\n }\n return trimmedValue\n }\n}\n\nfunction createBaseColumn(config: ColumnConfig, columnImpl: any) {\n const headerOptions: HeaderOptions = {\n showPinIcon: config.showPinIcon,\n hasDescription: !!config.description,\n }\n\n const width =\n config.customWidth ??\n calculateDefaultColumnWidth(\n config.columnName,\n undefined, // propertyInfo is not available here, passed from higher level\n headerOptions,\n )\n\n const keyed = keyColumn(config.columnName, columnImpl)\n return {\n ...keyed,\n component: withChangeIndicatorTooltip(keyed.component!, config.columnName),\n title: (\n <ColumnHeaderWithTooltip\n name={config.columnName}\n description={config.description}\n isRequired={config.isRequired}\n isUpsertKey={config.isUpsertKey}\n showPinIcon={config.showPinIcon}\n isPinned={config.isPinned}\n onTogglePin={config.onTogglePin}\n />\n ),\n headerClassName: getHeaderClassName(\n config.isRequired,\n !!config.isUpsertKey,\n ),\n minWidth: width,\n basis: width,\n grow: 0,\n shrink: 0,\n disabled: config.disabled,\n deleteValue: createDeleteValue(config.columnName, config.isRequired),\n pasteValue: wrapPasteValueWithSchemaCoercion(\n keyed.pasteValue,\n config.columnName,\n config.schemaPropertyInfo,\n ),\n stickyLeft: config.isPinned,\n }\n}\n\nconst COLUMN_FACTORIES = {\n multipleEnum: (config: ColumnConfig) => {\n return createBaseColumn(\n config,\n autocompleteMultipleEnumColumn({\n choices: config.enumeratedValues ?? [],\n colType: config.typeInfo?.type || null,\n limitTags: 3,\n clearValue: getEmptyValue(config.isRequired),\n }),\n )\n },\n\n boolean: (config: ColumnConfig) => {\n return createBaseColumn(\n config,\n autocompleteColumn({\n choices: [true, false],\n colType: 'boolean',\n clearValue: getEmptyValue(config.isRequired),\n }),\n )\n },\n\n number: (config: ColumnConfig) => {\n return createBaseColumn(config, floatColumn)\n },\n\n enumerated: (config: ColumnConfig) => {\n return createBaseColumn(\n config,\n autocompleteColumn({\n choices: config.enumeratedValues ?? [],\n colType: config.typeInfo?.type || null,\n clearValue: getEmptyValue(config.isRequired),\n }),\n )\n },\n\n 'date-time': (config: ColumnConfig) => {\n const columnImpl = dateTimeColumn({\n colType: config.typeInfo?.type || null,\n })\n\n // Date-time needs special width calculation\n const headerOptions: HeaderOptions = {\n showPinIcon: config.showPinIcon,\n hasDescription: !!config.description,\n }\n\n const width =\n config.customWidth ??\n calculateDefaultColumnWidth(\n config.columnName,\n {\n type: config.typeInfo ?? undefined,\n isRequired: config.isRequired,\n enumeratedValues: null,\n },\n headerOptions,\n )\n\n return {\n ...createBaseColumn(config, columnImpl),\n minWidth: width,\n basis: width,\n }\n },\n\n text: (config: ColumnConfig) => {\n const columnImpl = createTextColumn({\n continuousUpdates: false,\n deletedValue: undefined,\n parseUserInput: createParseUserInput(config.isRequired),\n })\n\n return {\n ...createBaseColumn(config, columnImpl),\n cellClassName: 'MuiInputBase-input',\n }\n },\n}\n\nfunction getColumnType(\n typeInfo: FlatTypeInfo | null,\n enumeratedValues?: EnumeratedValue[] | string[] | null,\n): keyof typeof COLUMN_FACTORIES {\n if (!typeInfo) {\n return enumeratedValues && enumeratedValues.length > 0\n ? 'enumerated'\n : 'text'\n }\n\n if (typeInfo.format === 'date-time') {\n return 'date-time'\n }\n\n // Handle arrays - check if it's an array of enums\n if (typeInfo.isArray) {\n return 'multipleEnum'\n }\n\n if (typeInfo.type === 'boolean') {\n return 'boolean'\n }\n\n if (typeInfo.type === 'number' || typeInfo.type === 'integer') {\n return 'number'\n }\n\n if (enumeratedValues && enumeratedValues.length > 0) {\n return 'enumerated'\n }\n\n return 'text'\n}\n\nexport function createColumn(config: ColumnConfig): Column {\n const columnType = getColumnType(config.typeInfo, config.enumeratedValues)\n return COLUMN_FACTORIES[columnType](config)\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Column } from '@sage-bionetworks/react-datasheet-grid';
|
|
2
2
|
import { SchemaPropertiesMap } from '@/utils/jsonschema/getSchemaPropertyInfo';
|
|
3
|
-
export declare function modelColsToGrid(columnNames: string[], columnOrder: number[], schemaPropertiesInfo: SchemaPropertiesMap, columnWidths?: Record<string, number>, pinnedColumns?: Set<number>, onTogglePin?: (columnIndex: number) => void): Column[];
|
|
3
|
+
export declare function modelColsToGrid(columnNames: string[], columnOrder: number[], schemaPropertiesInfo: SchemaPropertiesMap, columnWidths?: Record<string, number>, pinnedColumns?: Set<number>, onTogglePin?: (columnIndex: number) => void, upsertKey?: string[]): Column[];
|
|
4
4
|
//# sourceMappingURL=modelColsToGrid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelColsToGrid.d.ts","sourceRoot":"","sources":["../../../../src/components/DataGrid/utils/modelColsToGrid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wCAAwC,CAAA;AAE/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAA;AAE9E,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,MAAM,EAAE,EACrB,oBAAoB,EAAE,mBAAmB,EACzC,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACzC,aAAa,GAAE,GAAG,CAAC,MAAM,CAAa,EACtC,WAAW,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"modelColsToGrid.d.ts","sourceRoot":"","sources":["../../../../src/components/DataGrid/utils/modelColsToGrid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wCAAwC,CAAA;AAE/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAA;AAE9E,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,MAAM,EAAE,EACrB,oBAAoB,EAAE,mBAAmB,EACzC,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACzC,aAAa,GAAE,GAAG,CAAC,MAAM,CAAa,EACtC,WAAW,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EAC3C,SAAS,CAAC,EAAE,MAAM,EAAE,GACnB,MAAM,EAAE,CAwBV"}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { createColumn as e } from "./columnFactory.js";
|
|
2
2
|
//#region src/components/DataGrid/utils/modelColsToGrid.ts
|
|
3
|
-
function t(t, n, r, i = {}, a = /* @__PURE__ */ new Set(), o) {
|
|
4
|
-
return n.map((n,
|
|
5
|
-
let
|
|
3
|
+
function t(t, n, r, i = {}, a = /* @__PURE__ */ new Set(), o, s) {
|
|
4
|
+
return n.map((n, c) => {
|
|
5
|
+
let l = t[n], u = r[l], d = c === 0 && o !== void 0, f = a.has(c);
|
|
6
6
|
return e({
|
|
7
|
-
columnName:
|
|
8
|
-
description:
|
|
9
|
-
typeInfo:
|
|
10
|
-
enumeratedValues:
|
|
11
|
-
isRequired:
|
|
12
|
-
disabled:
|
|
13
|
-
customWidth: i[
|
|
14
|
-
showPinIcon:
|
|
15
|
-
isPinned:
|
|
16
|
-
onTogglePin: o ? () => o(
|
|
17
|
-
schemaPropertyInfo:
|
|
7
|
+
columnName: l,
|
|
8
|
+
description: u?.description,
|
|
9
|
+
typeInfo: u?.type || null,
|
|
10
|
+
enumeratedValues: u?.enumeratedValues || [],
|
|
11
|
+
isRequired: u?.isRequired || !1,
|
|
12
|
+
disabled: u?.type?.readOnly,
|
|
13
|
+
customWidth: i[l],
|
|
14
|
+
showPinIcon: d,
|
|
15
|
+
isPinned: f,
|
|
16
|
+
onTogglePin: o ? () => o(c) : void 0,
|
|
17
|
+
schemaPropertyInfo: u,
|
|
18
|
+
isUpsertKey: s?.includes(l) ?? !1
|
|
18
19
|
});
|
|
19
20
|
});
|
|
20
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelColsToGrid.js","names":["Column","createColumn","SchemaPropertiesMap","modelColsToGrid","columnNames","columnOrder","schemaPropertiesInfo","columnWidths","Record","pinnedColumns","Set","onTogglePin","columnIndex","map","index","arrayIndex","columnName","propertyInfo","showPinIcon","undefined","isPinned","has","description","typeInfo","type","enumeratedValues","isRequired","disabled","readOnly","customWidth","schemaPropertyInfo"],"sources":["../../../../src/components/DataGrid/utils/modelColsToGrid.ts"],"sourcesContent":["import { Column } from '@sage-bionetworks/react-datasheet-grid'\nimport { createColumn } from './columnFactory'\nimport { SchemaPropertiesMap } from '@/utils/jsonschema/getSchemaPropertyInfo'\n\nexport function modelColsToGrid(\n columnNames: string[],\n columnOrder: number[],\n schemaPropertiesInfo: SchemaPropertiesMap,\n columnWidths: Record<string, number> = {},\n pinnedColumns: Set<number> = new Set(),\n onTogglePin?: (columnIndex: number) => void,\n): Column[] {\n return columnOrder.map((index: number, arrayIndex: number) => {\n const columnName = columnNames[index]\n const propertyInfo = schemaPropertiesInfo[columnName]\n\n // Only show pin functionality for the first column and when toggle handler exists\n const showPinIcon = arrayIndex === 0 && onTogglePin !== undefined\n const isPinned = pinnedColumns.has(arrayIndex)\n\n return createColumn({\n columnName,\n description: propertyInfo?.description,\n typeInfo: propertyInfo?.type || null,\n enumeratedValues: propertyInfo?.enumeratedValues || [],\n isRequired: propertyInfo?.isRequired || false,\n disabled: propertyInfo?.type?.readOnly,\n customWidth: columnWidths[columnName],\n showPinIcon,\n isPinned,\n onTogglePin: onTogglePin ? () => onTogglePin(arrayIndex) : undefined,\n schemaPropertyInfo: propertyInfo,\n })\n })\n}\n"],"mappings":";;AAIA,SAAgBG,EACdC,GACAC,GACAC,GACAC,IAAuC,CAAC,GACxCE,oBAA6B,IAAIC,IAAI,GACrCC,GACU;CACV,
|
|
1
|
+
{"version":3,"file":"modelColsToGrid.js","names":["Column","createColumn","SchemaPropertiesMap","modelColsToGrid","columnNames","columnOrder","schemaPropertiesInfo","columnWidths","Record","pinnedColumns","Set","onTogglePin","columnIndex","upsertKey","map","index","arrayIndex","columnName","propertyInfo","showPinIcon","undefined","isPinned","has","description","typeInfo","type","enumeratedValues","isRequired","disabled","readOnly","customWidth","schemaPropertyInfo","isUpsertKey","includes"],"sources":["../../../../src/components/DataGrid/utils/modelColsToGrid.ts"],"sourcesContent":["import { Column } from '@sage-bionetworks/react-datasheet-grid'\nimport { createColumn } from './columnFactory'\nimport { SchemaPropertiesMap } from '@/utils/jsonschema/getSchemaPropertyInfo'\n\nexport function modelColsToGrid(\n columnNames: string[],\n columnOrder: number[],\n schemaPropertiesInfo: SchemaPropertiesMap,\n columnWidths: Record<string, number> = {},\n pinnedColumns: Set<number> = new Set(),\n onTogglePin?: (columnIndex: number) => void,\n upsertKey?: string[],\n): Column[] {\n return columnOrder.map((index: number, arrayIndex: number) => {\n const columnName = columnNames[index]\n const propertyInfo = schemaPropertiesInfo[columnName]\n\n // Only show pin functionality for the first column and when toggle handler exists\n const showPinIcon = arrayIndex === 0 && onTogglePin !== undefined\n const isPinned = pinnedColumns.has(arrayIndex)\n\n return createColumn({\n columnName,\n description: propertyInfo?.description,\n typeInfo: propertyInfo?.type || null,\n enumeratedValues: propertyInfo?.enumeratedValues || [],\n isRequired: propertyInfo?.isRequired || false,\n disabled: propertyInfo?.type?.readOnly,\n customWidth: columnWidths[columnName],\n showPinIcon,\n isPinned,\n onTogglePin: onTogglePin ? () => onTogglePin(arrayIndex) : undefined,\n schemaPropertyInfo: propertyInfo,\n isUpsertKey: upsertKey?.includes(columnName) ?? false,\n })\n })\n}\n"],"mappings":";;AAIA,SAAgBG,EACdC,GACAC,GACAC,GACAC,IAAuC,CAAC,GACxCE,oBAA6B,IAAIC,IAAI,GACrCC,GACAE,GACU;CACV,OAAOR,EAAYS,KAAKC,GAAeC,MAAuB;EAC5D,IAAMC,IAAab,EAAYW,IACzBG,IAAeZ,EAAqBW,IAGpCE,IAAcH,MAAe,KAAKL,MAAgBS,KAAAA,GAClDC,IAAWZ,EAAca,IAAIN,CAAU;EAE7C,OAAOf,EAAa;GAClBgB;GACAM,aAAaL,GAAcK;GAC3BC,UAAUN,GAAcO,QAAQ;GAChCC,kBAAkBR,GAAcQ,oBAAoB,CAAA;GACpDC,YAAYT,GAAcS,cAAc;GACxCC,UAAUV,GAAcO,MAAMI;GAC9BC,aAAavB,EAAaU;GAC1BE;GACAE;GACAV,aAAaA,UAAoBA,EAAYK,CAAU,IAAII,KAAAA;GAC3DW,oBAAoBb;GACpBc,aAAanB,GAAWoB,SAAShB,CAAU,KAAK;EAClD,CAAC;CACH,CAAC;AACH"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server sends error notifications as plain objects `{message, code, errno}`.
|
|
3
|
+
* Convert those to proper Error instances so error boundaries can render the message.
|
|
4
|
+
* Non-conforming values are returned unchanged.
|
|
5
|
+
*/
|
|
6
|
+
export declare function normalizeWebsocketError(error: unknown): unknown;
|
|
7
|
+
//# sourceMappingURL=normalizeWebsocketError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeWebsocketError.d.ts","sourceRoot":"","sources":["../../../../src/components/DataGrid/utils/normalizeWebsocketError.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAU/D"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/components/DataGrid/utils/normalizeWebsocketError.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
return typeof e == "object" && e && "message" in e && typeof e.message == "string" ? Error(e.message) : e;
|
|
4
|
+
}
|
|
5
|
+
//#endregion
|
|
6
|
+
export { e as normalizeWebsocketError };
|
|
7
|
+
|
|
8
|
+
//# sourceMappingURL=normalizeWebsocketError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeWebsocketError.js","names":["normalizeWebsocketError","error","message","Error"],"sources":["../../../../src/components/DataGrid/utils/normalizeWebsocketError.ts"],"sourcesContent":["/**\n * The server sends error notifications as plain objects `{message, code, errno}`.\n * Convert those to proper Error instances so error boundaries can render the message.\n * Non-conforming values are returned unchanged.\n */\nexport function normalizeWebsocketError(error: unknown): unknown {\n if (\n error != null &&\n typeof error === 'object' &&\n 'message' in error &&\n typeof (error as { message: unknown }).message === 'string'\n ) {\n return new Error((error as { message: string }).message)\n }\n return error\n}\n"],"mappings":";AAKA,SAAgBA,EAAwBC,GAAyB;CAS/D,OANE,OAAOA,KAAU,YADjBA,KAEA,aAAaA,KACb,OAAQA,EAA+BC,WAAY,WAExCC,MAAOF,EAA8BC,OAAO,IAElDD;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityCitation.d.ts","sourceRoot":"","sources":["../../../src/components/EntityCitation/EntityCitation.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAWzB,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,QAAA,MAAM,cAAc,GAAI,yCAIrB,mBAAmB,KAAG,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"EntityCitation.d.ts","sourceRoot":"","sources":["../../../src/components/EntityCitation/EntityCitation.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAWzB,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,QAAA,MAAM,cAAc,GAAI,yCAIrB,mBAAmB,KAAG,KAAK,CAAC,SAyJ9B,CAAA;AAED,eAAe,cAAc,CAAA"}
|
|
@@ -30,10 +30,21 @@ var m = ({ projectId: m, entityId: h, versionNumber: g }) => {
|
|
|
30
30
|
disabled: v && j,
|
|
31
31
|
options: ["Cite this Project", "Cite only this page"],
|
|
32
32
|
anchorRef: D,
|
|
33
|
-
sx: {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
sx: {
|
|
34
|
+
width: {
|
|
35
|
+
xs: "100%",
|
|
36
|
+
sm: "initial"
|
|
37
|
+
},
|
|
38
|
+
"& .MuiButtonGroup-firstButton": {
|
|
39
|
+
borderTopLeftRadius: "6px",
|
|
40
|
+
borderBottomLeftRadius: "6px"
|
|
41
|
+
},
|
|
42
|
+
"& .MuiButtonGroup-lastButton": {
|
|
43
|
+
borderTopRightRadius: "6px",
|
|
44
|
+
borderBottomRightRadius: "6px"
|
|
45
|
+
},
|
|
46
|
+
"& .MuiButton-root": { fontWeight: 540 }
|
|
47
|
+
},
|
|
37
48
|
buttonText: "Cite as...",
|
|
38
49
|
variant: "outlined",
|
|
39
50
|
buttonGroupAriaLabel: "Citation options",
|
|
@@ -48,10 +59,14 @@ var m = ({ projectId: m, entityId: h, versionNumber: g }) => {
|
|
|
48
59
|
k && !A ? (S(k.doiUri), w(e.currentTarget)) : !k && A ? (S(A.doiUri), w(e.currentTarget)) : b(e.currentTarget);
|
|
49
60
|
},
|
|
50
61
|
disabled: v && j,
|
|
51
|
-
sx: {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
sx: {
|
|
63
|
+
width: {
|
|
64
|
+
xs: "100%",
|
|
65
|
+
sm: "initial"
|
|
66
|
+
},
|
|
67
|
+
borderRadius: "6px",
|
|
68
|
+
fontWeight: 540
|
|
69
|
+
},
|
|
55
70
|
variant: "outlined",
|
|
56
71
|
startIcon: /* @__PURE__ */ d(n, {
|
|
57
72
|
width: 18,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityCitation.js","names":[],"sources":["../../../src/components/EntityCitation/EntityCitation.tsx"],"sourcesContent":["import React from 'react'\nimport { ReactComponent as DoubleQuotes } from '@/assets/icons/DoubleQuotes.svg'\nimport { Button, Skeleton } from '@mui/material'\nimport { useGetDOIAssociation } from '@/synapse-queries/doi/useDOI'\nimport { useGetEntityBundle } from '@/synapse-queries/entity'\nimport { DoiObjectType } from '@sage-bionetworks/synapse-client'\nimport CitationPopoverContent from '../CitationPopover/CitationPopoverContent'\nimport { useRef, useState } from 'react'\nimport { useGetEntityDoiAssociation } from './EntityCitationUtils'\nimport DropdownSelect from '../DropdownSelect'\n\nexport type EntityCitationProps = {\n projectId: string\n entityId: string\n versionNumber?: number\n}\n\nconst EntityCitation = ({\n projectId,\n entityId,\n versionNumber,\n}: EntityCitationProps): React.ReactNode => {\n const { data: bundle, isLoading: isEntityLoading } = useGetEntityBundle(\n entityId,\n versionNumber,\n )\n const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null)\n const [selectedDoi, setSelectedDoi] = useState<string | undefined>(undefined)\n const [citationAnchorEl, setCitationAnchorEl] = useState<HTMLElement | null>(\n null,\n )\n const [selectedIndex, setSelectedIndex] = useState<number | undefined>(\n undefined,\n )\n const citationButtonRef = useRef<HTMLDivElement>(null)\n\n const open = Boolean(anchorEl)\n const id = open ? 'cite-as-popover' : undefined\n\n const entityDoiAssociation = useGetEntityDoiAssociation(\n entityId,\n versionNumber,\n projectId,\n bundle,\n )\n\n // Get DOI association for the project\n // Projects are not versioned\n const { data: projectDoiAssociation, isLoading: isProjectLoading } =\n useGetDOIAssociation(\n {\n id: projectId,\n version: undefined,\n type: DoiObjectType.ENTITY,\n },\n {\n enabled: !!projectId,\n },\n )\n\n // If both project and entity have DOIs, we show the dropdown variant\n const isProjectAndEntityDoi = projectDoiAssociation && entityDoiAssociation\n\n function getButtonText() {\n if (isProjectAndEntityDoi) {\n return 'Cite as...'\n } else if (entityDoiAssociation && !projectDoiAssociation) {\n return 'Cite page'\n } else if (!entityDoiAssociation && projectDoiAssociation) {\n return 'Cite project'\n }\n return ''\n }\n\n const buttonText = getButtonText()\n\n const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (entityDoiAssociation && !projectDoiAssociation) {\n setSelectedDoi(entityDoiAssociation.doiUri)\n setCitationAnchorEl(event.currentTarget)\n } else if (!entityDoiAssociation && projectDoiAssociation) {\n setSelectedDoi(projectDoiAssociation.doiUri)\n setCitationAnchorEl(event.currentTarget)\n } else {\n // If both project and entity doi exist, show dropdown\n setAnchorEl(event.currentTarget)\n }\n }\n\n const popover = (\n <CitationPopoverContent\n doi={selectedDoi}\n anchorEl={citationAnchorEl}\n open={!!citationAnchorEl && !!selectedDoi}\n id={id}\n onClose={() => {\n setCitationAnchorEl(null)\n setSelectedDoi(undefined)\n }}\n />\n )\n\n const options: string[] = ['Cite this Project', 'Cite only this page']\n\n if (!projectDoiAssociation && !entityDoiAssociation) {\n return null\n }\n\n if (isEntityLoading || isProjectLoading) {\n return (\n <Skeleton\n sx={{\n width: { xs: '100%', sm: '140px' },\n height: '45px',\n }}\n />\n )\n }\n\n return (\n <>\n {isProjectAndEntityDoi ? (\n <DropdownSelect\n disabled={isEntityLoading && isProjectLoading}\n options={options}\n anchorRef={citationButtonRef}\n sx={{\n width: { xs: '100%', sm: 'initial' },\n }}\n buttonText=\"Cite as...\"\n variant=\"outlined\"\n buttonGroupAriaLabel=\"Citation options\"\n selectedIndex={selectedIndex}\n setSelectedIndex={(index: number) => {\n setSelectedIndex(index)\n const selected =\n index === 0\n ? projectDoiAssociation?.doiUri\n : entityDoiAssociation?.doiUri\n if (selected) {\n setSelectedDoi(selected)\n setCitationAnchorEl(citationButtonRef.current)\n }\n }}\n />\n ) : (\n <Button\n onClick={handleButtonClick}\n disabled={isEntityLoading && isProjectLoading}\n sx={{\n width: { xs: '100%', sm: 'initial' },\n }}\n variant=\"outlined\"\n startIcon={<DoubleQuotes width={18} height={18} />}\n >\n {buttonText}\n </Button>\n )}\n {popover}\n </>\n )\n}\n\nexport default EntityCitation\n"],"mappings":";;;;;;;;;;;;AAiBA,IAAM,KAAkB,EACtB,cACA,aACA,uBAC0C;CAC1C,IAAM,EAAE,MAAM,GAAQ,WAAW,MAAoB,EACnD,GACA,CACF,GACM,CAAC,GAAU,KAAe,EAAmC,IAAI,GACjE,CAAC,GAAa,KAAkB,EAA6B,KAAA,CAAS,GACtE,CAAC,GAAkB,KAAuB,EAC9C,IACF,GACM,CAAC,GAAe,KAAoB,EACxC,KAAA,CACF,GACM,IAAoB,EAAuB,IAAI,GAG/C,IADe,IACH,oBAAoB,KAAA,GAEhC,IAAuB,EAC3B,GACA,GACA,GACA,CACF,GAIM,EAAE,MAAM,GAAuB,WAAW,MAC9C,EACE;EACE,IAAI;EACJ,SAAS,KAAA;EACT,MAAM,EAAc;CACtB,GACA,EACE,SAAS,CAAC,CAAC,EACb,CACF,GAGI,IAAwB,KAAyB;CAEvD,SAAS,IAAgB;EAQvB,OAPI,IACK,eACE,KAAwB,CAAC,IAC3B,cACE,CAAC,KAAwB,IAC3B,iBAEF;CACT;CAEA,IAAM,IAAa,EAAc;CA6CjC,OAfI,CAAC,KAAyB,CAAC,IACtB,OAGL,KAAmB,IAEnB,kBAAC,GAAD,EACE,IAAI;EACF,OAAO;GAAE,IAAI;GAAQ,IAAI;EAAQ;EACjC,QAAQ;CACV,EACD,CAAA,IAKH,kBAAA,GAAA,EAAA,UAAA,CACG,IACC,kBAAC,GAAD;EACE,UAAU,KAAmB;EACpB,UAtBU,qBAAqB,qBAsB/B;EACT,WAAW;EACX,IAAI,
|
|
1
|
+
{"version":3,"file":"EntityCitation.js","names":[],"sources":["../../../src/components/EntityCitation/EntityCitation.tsx"],"sourcesContent":["import React from 'react'\nimport { ReactComponent as DoubleQuotes } from '@/assets/icons/DoubleQuotes.svg'\nimport { Button, Skeleton } from '@mui/material'\nimport { useGetDOIAssociation } from '@/synapse-queries/doi/useDOI'\nimport { useGetEntityBundle } from '@/synapse-queries/entity'\nimport { DoiObjectType } from '@sage-bionetworks/synapse-client'\nimport CitationPopoverContent from '../CitationPopover/CitationPopoverContent'\nimport { useRef, useState } from 'react'\nimport { useGetEntityDoiAssociation } from './EntityCitationUtils'\nimport DropdownSelect from '../DropdownSelect'\n\nexport type EntityCitationProps = {\n projectId: string\n entityId: string\n versionNumber?: number\n}\n\nconst EntityCitation = ({\n projectId,\n entityId,\n versionNumber,\n}: EntityCitationProps): React.ReactNode => {\n const { data: bundle, isLoading: isEntityLoading } = useGetEntityBundle(\n entityId,\n versionNumber,\n )\n const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null)\n const [selectedDoi, setSelectedDoi] = useState<string | undefined>(undefined)\n const [citationAnchorEl, setCitationAnchorEl] = useState<HTMLElement | null>(\n null,\n )\n const [selectedIndex, setSelectedIndex] = useState<number | undefined>(\n undefined,\n )\n const citationButtonRef = useRef<HTMLDivElement>(null)\n\n const open = Boolean(anchorEl)\n const id = open ? 'cite-as-popover' : undefined\n\n const entityDoiAssociation = useGetEntityDoiAssociation(\n entityId,\n versionNumber,\n projectId,\n bundle,\n )\n\n // Get DOI association for the project\n // Projects are not versioned\n const { data: projectDoiAssociation, isLoading: isProjectLoading } =\n useGetDOIAssociation(\n {\n id: projectId,\n version: undefined,\n type: DoiObjectType.ENTITY,\n },\n {\n enabled: !!projectId,\n },\n )\n\n // If both project and entity have DOIs, we show the dropdown variant\n const isProjectAndEntityDoi = projectDoiAssociation && entityDoiAssociation\n\n function getButtonText() {\n if (isProjectAndEntityDoi) {\n return 'Cite as...'\n } else if (entityDoiAssociation && !projectDoiAssociation) {\n return 'Cite page'\n } else if (!entityDoiAssociation && projectDoiAssociation) {\n return 'Cite project'\n }\n return ''\n }\n\n const buttonText = getButtonText()\n\n const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (entityDoiAssociation && !projectDoiAssociation) {\n setSelectedDoi(entityDoiAssociation.doiUri)\n setCitationAnchorEl(event.currentTarget)\n } else if (!entityDoiAssociation && projectDoiAssociation) {\n setSelectedDoi(projectDoiAssociation.doiUri)\n setCitationAnchorEl(event.currentTarget)\n } else {\n // If both project and entity doi exist, show dropdown\n setAnchorEl(event.currentTarget)\n }\n }\n\n const popover = (\n <CitationPopoverContent\n doi={selectedDoi}\n anchorEl={citationAnchorEl}\n open={!!citationAnchorEl && !!selectedDoi}\n id={id}\n onClose={() => {\n setCitationAnchorEl(null)\n setSelectedDoi(undefined)\n }}\n />\n )\n\n const options: string[] = ['Cite this Project', 'Cite only this page']\n\n if (!projectDoiAssociation && !entityDoiAssociation) {\n return null\n }\n\n if (isEntityLoading || isProjectLoading) {\n return (\n <Skeleton\n sx={{\n width: { xs: '100%', sm: '140px' },\n height: '45px',\n }}\n />\n )\n }\n\n return (\n <>\n {isProjectAndEntityDoi ? (\n <DropdownSelect\n disabled={isEntityLoading && isProjectLoading}\n options={options}\n anchorRef={citationButtonRef}\n sx={{\n width: { xs: '100%', sm: 'initial' },\n '& .MuiButtonGroup-firstButton': {\n borderTopLeftRadius: '6px',\n borderBottomLeftRadius: '6px',\n },\n '& .MuiButtonGroup-lastButton': {\n borderTopRightRadius: '6px',\n borderBottomRightRadius: '6px',\n },\n '& .MuiButton-root': {\n fontWeight: 540,\n },\n }}\n buttonText=\"Cite as...\"\n variant=\"outlined\"\n buttonGroupAriaLabel=\"Citation options\"\n selectedIndex={selectedIndex}\n setSelectedIndex={(index: number) => {\n setSelectedIndex(index)\n const selected =\n index === 0\n ? projectDoiAssociation?.doiUri\n : entityDoiAssociation?.doiUri\n if (selected) {\n setSelectedDoi(selected)\n setCitationAnchorEl(citationButtonRef.current)\n }\n }}\n />\n ) : (\n <Button\n onClick={handleButtonClick}\n disabled={isEntityLoading && isProjectLoading}\n sx={{\n width: { xs: '100%', sm: 'initial' },\n borderRadius: '6px',\n fontWeight: 540,\n }}\n variant=\"outlined\"\n startIcon={<DoubleQuotes width={18} height={18} />}\n >\n {buttonText}\n </Button>\n )}\n {popover}\n </>\n )\n}\n\nexport default EntityCitation\n"],"mappings":";;;;;;;;;;;;AAiBA,IAAM,KAAkB,EACtB,cACA,aACA,uBAC0C;CAC1C,IAAM,EAAE,MAAM,GAAQ,WAAW,MAAoB,EACnD,GACA,CACF,GACM,CAAC,GAAU,KAAe,EAAmC,IAAI,GACjE,CAAC,GAAa,KAAkB,EAA6B,KAAA,CAAS,GACtE,CAAC,GAAkB,KAAuB,EAC9C,IACF,GACM,CAAC,GAAe,KAAoB,EACxC,KAAA,CACF,GACM,IAAoB,EAAuB,IAAI,GAG/C,IADe,IACH,oBAAoB,KAAA,GAEhC,IAAuB,EAC3B,GACA,GACA,GACA,CACF,GAIM,EAAE,MAAM,GAAuB,WAAW,MAC9C,EACE;EACE,IAAI;EACJ,SAAS,KAAA;EACT,MAAM,EAAc;CACtB,GACA,EACE,SAAS,CAAC,CAAC,EACb,CACF,GAGI,IAAwB,KAAyB;CAEvD,SAAS,IAAgB;EAQvB,OAPI,IACK,eACE,KAAwB,CAAC,IAC3B,cACE,CAAC,KAAwB,IAC3B,iBAEF;CACT;CAEA,IAAM,IAAa,EAAc;CA6CjC,OAfI,CAAC,KAAyB,CAAC,IACtB,OAGL,KAAmB,IAEnB,kBAAC,GAAD,EACE,IAAI;EACF,OAAO;GAAE,IAAI;GAAQ,IAAI;EAAQ;EACjC,QAAQ;CACV,EACD,CAAA,IAKH,kBAAA,GAAA,EAAA,UAAA,CACG,IACC,kBAAC,GAAD;EACE,UAAU,KAAmB;EACpB,UAtBU,qBAAqB,qBAsB/B;EACT,WAAW;EACX,IAAI;GACF,OAAO;IAAE,IAAI;IAAQ,IAAI;GAAU;GACnC,iCAAiC;IAC/B,qBAAqB;IACrB,wBAAwB;GAC1B;GACA,gCAAgC;IAC9B,sBAAsB;IACtB,yBAAyB;GAC3B;GACA,qBAAqB,EACnB,YAAY,IACd;EACF;EACA,YAAW;EACX,SAAQ;EACR,sBAAqB;EACN;EACf,mBAAmB,MAAkB;GACnC,EAAiB,CAAK;GACtB,IAAM,IACJ,MAAU,IACN,GAAuB,SACvB,GAAsB;GAC5B,AAAI,MACF,EAAe,CAAQ,GACvB,EAAoB,EAAkB,OAAO;EAEjD;CACD,CAAA,IAED,kBAAC,GAAD;EACE,UAlFmB,MAA+C;GACxE,AAAI,KAAwB,CAAC,KAC3B,EAAe,EAAqB,MAAM,GAC1C,EAAoB,EAAM,aAAa,KAC9B,CAAC,KAAwB,KAClC,EAAe,EAAsB,MAAM,GAC3C,EAAoB,EAAM,aAAa,KAGvC,EAAY,EAAM,aAAa;EAEnC;EAwEQ,UAAU,KAAmB;EAC7B,IAAI;GACF,OAAO;IAAE,IAAI;IAAQ,IAAI;GAAU;GACnC,cAAc;GACd,YAAY;EACd;EACA,SAAQ;EACR,WAAW,kBAAC,GAAD;GAAc,OAAO;GAAI,QAAQ;EAAK,CAAA;YAEhD;CACK,CAAA,GAET,gBAjFH,EAAC,GAAD;EACE,KAAK;EACL,UAAU;EACV,MAAM,CAAC,CAAC,KAAoB,CAAC,CAAC;EAC1B;EACJ,eAAe;GAEb,AADA,EAAoB,IAAI,GACxB,EAAe,KAAA,CAAS;EAC1B;CACD,CAwEE,CACD,EAAA,CAAA;AAEN"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RenderedFacetValueChip } from '@/components/QueryVisualizationWrapper/QueryVisualizationContext';
|
|
2
|
+
export type DuoTermTagsProps = {
|
|
3
|
+
/** Raw `dataUseModifiers` values (ontology codes or term names). */
|
|
4
|
+
terms: string[];
|
|
5
|
+
};
|
|
6
|
+
/** Renders a dataset's DUO data-use modifiers as a row of chips (used on cards). */
|
|
7
|
+
export default function DuoTermTags(props: DuoTermTagsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
+
/**
|
|
9
|
+
* Builds a `renderFacetValue` function (for `QueryVisualizationWrapper`) that
|
|
10
|
+
* returns the chip content for the given DUO column's enumeration facet values
|
|
11
|
+
* — used by both the facet sidebar and the active-filter pills, which render it
|
|
12
|
+
* through the shared `FacetValueChip`. Other columns fall through to text.
|
|
13
|
+
*
|
|
14
|
+
* Portals only need to declare the DUO column (via the card schema's
|
|
15
|
+
* `dataUseModifiersColumnName`) rather than each defining their own handler.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createDuoFacetValueRenderer(duoColumnName: string): (columnName: string, value: string) => RenderedFacetValueChip | undefined;
|
|
18
|
+
//# sourceMappingURL=DuoTermTags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DuoTermTags.d.ts","sourceRoot":"","sources":["../../../../src/components/GenericCard/DuoTermTags/DuoTermTags.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kEAAkE,CAAA;AAE9G,MAAM,MAAM,gBAAgB,GAAG;IAC7B,oEAAoE;IACpE,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AA4ED,oFAAoF;AACpF,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,KAAK,EAAE,gBAAgB,kDA6B1D;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,IAE7D,YAAY,MAAM,EAClB,OAAO,MAAM,KACZ,sBAAsB,GAAG,SAAS,CAWtC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import e from "../../../assets/icons/GeneticsIcon.js";
|
|
2
|
+
import { duoRegistryUrl as t, resolveDuoTerm as n } from "./duoTerms.js";
|
|
3
|
+
import r from "../../widgets/facet-nav/FacetValueChip.js";
|
|
4
|
+
import { Box as i, Link as a } from "@mui/material";
|
|
5
|
+
import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
6
|
+
import l from "@mui/icons-material/ScienceOutlined";
|
|
7
|
+
import u from "@mui/icons-material/MedicalServicesOutlined";
|
|
8
|
+
import d from "@mui/icons-material/CoronavirusOutlined";
|
|
9
|
+
import f from "@mui/icons-material/GroupsOutlined";
|
|
10
|
+
import p from "@mui/icons-material/HealingOutlined";
|
|
11
|
+
import m from "@mui/icons-material/LockOpenOutlined";
|
|
12
|
+
import h from "@mui/icons-material/MoneyOffOutlined";
|
|
13
|
+
import g from "@mui/icons-material/ArticleOutlined";
|
|
14
|
+
import _ from "@mui/icons-material/HandshakeOutlined";
|
|
15
|
+
import v from "@mui/icons-material/GavelOutlined";
|
|
16
|
+
import y from "@mui/icons-material/ReplayOutlined";
|
|
17
|
+
import b from "@mui/icons-material/FormatQuoteOutlined";
|
|
18
|
+
import x from "@mui/icons-material/PersonOutlined";
|
|
19
|
+
import S from "@mui/icons-material/AssignmentOutlined";
|
|
20
|
+
import C from "@mui/icons-material/ApartmentOutlined";
|
|
21
|
+
import w from "@mui/icons-material/PublicOutlined";
|
|
22
|
+
import T from "@mui/icons-material/ScheduleOutlined";
|
|
23
|
+
import E from "@mui/icons-material/HourglassEmptyOutlined";
|
|
24
|
+
import D from "@mui/icons-material/BlockOutlined";
|
|
25
|
+
import O from "@mui/icons-material/RuleOutlined";
|
|
26
|
+
import k from "@mui/icons-material/HelpOutline";
|
|
27
|
+
//#region src/components/GenericCard/DuoTermTags/DuoTermTags.tsx
|
|
28
|
+
var A = {
|
|
29
|
+
"DUO:0000042": l,
|
|
30
|
+
"DUO:0000006": u,
|
|
31
|
+
"DUO:0000007": d,
|
|
32
|
+
"DUO:0000011": f,
|
|
33
|
+
"DUO:0000043": p,
|
|
34
|
+
"DUO:0000004": m,
|
|
35
|
+
"DUO:0000046": h,
|
|
36
|
+
"DUO:0000018": h,
|
|
37
|
+
"DUO:0000045": h,
|
|
38
|
+
"DUO:0000019": g,
|
|
39
|
+
"DUO:0000020": _,
|
|
40
|
+
"DUO:0000021": v,
|
|
41
|
+
"DUO:0000029": y,
|
|
42
|
+
DUOplus7: b,
|
|
43
|
+
"DUO:0000026": x,
|
|
44
|
+
"DUO:0000027": S,
|
|
45
|
+
"DUO:0000028": C,
|
|
46
|
+
"DUO:0000022": w,
|
|
47
|
+
"DUO:0000025": T,
|
|
48
|
+
"DUO:0000024": E,
|
|
49
|
+
"DUO:0000016": e,
|
|
50
|
+
"DUO:0000015": D,
|
|
51
|
+
"DUO:0000044": D,
|
|
52
|
+
"DUO:0000012": O
|
|
53
|
+
}, j = (e) => {
|
|
54
|
+
let { term: t } = e;
|
|
55
|
+
return /* @__PURE__ */ s(A[t.code] ?? k, {});
|
|
56
|
+
}, M = {
|
|
57
|
+
permission: 0,
|
|
58
|
+
commercial: 1,
|
|
59
|
+
obligation: 2,
|
|
60
|
+
limit: 3
|
|
61
|
+
};
|
|
62
|
+
function N(e) {
|
|
63
|
+
let n = t(e), r = !!(e.abbr || e.code);
|
|
64
|
+
return /* @__PURE__ */ c(o, { children: [
|
|
65
|
+
/* @__PURE__ */ s("strong", { children: e.name }),
|
|
66
|
+
r && /* @__PURE__ */ c(o, { children: [
|
|
67
|
+
/* @__PURE__ */ s("br", {}),
|
|
68
|
+
e.abbr,
|
|
69
|
+
e.abbr && e.code ? " · " : "",
|
|
70
|
+
n ? /* @__PURE__ */ s(a, {
|
|
71
|
+
href: n,
|
|
72
|
+
target: "_blank",
|
|
73
|
+
rel: "noopener",
|
|
74
|
+
color: "inherit",
|
|
75
|
+
children: e.code
|
|
76
|
+
}) : e.code
|
|
77
|
+
] }),
|
|
78
|
+
e.definition && e.definition !== e.name && /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s("br", {}), e.definition] })
|
|
79
|
+
] });
|
|
80
|
+
}
|
|
81
|
+
function P(e) {
|
|
82
|
+
let { terms: t } = e;
|
|
83
|
+
return !t || t.length === 0 ? null : /* @__PURE__ */ s(i, {
|
|
84
|
+
sx: {
|
|
85
|
+
display: "flex",
|
|
86
|
+
flexWrap: "wrap",
|
|
87
|
+
gap: .75,
|
|
88
|
+
minWidth: 0,
|
|
89
|
+
maxWidth: "100%"
|
|
90
|
+
},
|
|
91
|
+
children: t.map(n).sort((e, t) => M[e.category] - M[t.category]).map((e, t) => /* @__PURE__ */ s(r, {
|
|
92
|
+
label: e.name,
|
|
93
|
+
icon: /* @__PURE__ */ s(j, { term: e }),
|
|
94
|
+
tooltipTitle: N(e)
|
|
95
|
+
}, t))
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function F(e) {
|
|
99
|
+
return (t, r) => {
|
|
100
|
+
if (t !== e) return;
|
|
101
|
+
let i = n(r);
|
|
102
|
+
return {
|
|
103
|
+
label: i.name,
|
|
104
|
+
icon: /* @__PURE__ */ s(j, { term: i }),
|
|
105
|
+
tooltipTitle: N(i)
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { F as createDuoFacetValueRenderer, P as default };
|
|
111
|
+
|
|
112
|
+
//# sourceMappingURL=DuoTermTags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DuoTermTags.js","names":[],"sources":["../../../../src/components/GenericCard/DuoTermTags/DuoTermTags.tsx"],"sourcesContent":["import { Box, Link } from '@mui/material'\nimport type { ComponentType, ReactNode } from 'react'\nimport GeneticsIcon from '@/assets/icons/GeneticsIcon'\nimport ScienceOutlined from '@mui/icons-material/ScienceOutlined'\nimport MedicalServicesOutlined from '@mui/icons-material/MedicalServicesOutlined'\nimport CoronavirusOutlined from '@mui/icons-material/CoronavirusOutlined'\nimport GroupsOutlined from '@mui/icons-material/GroupsOutlined'\nimport HealingOutlined from '@mui/icons-material/HealingOutlined'\nimport LockOpenOutlined from '@mui/icons-material/LockOpenOutlined'\nimport MoneyOffOutlined from '@mui/icons-material/MoneyOffOutlined'\nimport ArticleOutlined from '@mui/icons-material/ArticleOutlined'\nimport HandshakeOutlined from '@mui/icons-material/HandshakeOutlined'\nimport GavelOutlined from '@mui/icons-material/GavelOutlined'\nimport ReplayOutlined from '@mui/icons-material/ReplayOutlined'\nimport FormatQuoteOutlined from '@mui/icons-material/FormatQuoteOutlined'\nimport PersonOutlined from '@mui/icons-material/PersonOutlined'\nimport AssignmentOutlined from '@mui/icons-material/AssignmentOutlined'\nimport ApartmentOutlined from '@mui/icons-material/ApartmentOutlined'\nimport PublicOutlined from '@mui/icons-material/PublicOutlined'\nimport ScheduleOutlined from '@mui/icons-material/ScheduleOutlined'\nimport HourglassEmptyOutlined from '@mui/icons-material/HourglassEmptyOutlined'\nimport BlockOutlined from '@mui/icons-material/BlockOutlined'\nimport RuleOutlined from '@mui/icons-material/RuleOutlined'\nimport HelpOutline from '@mui/icons-material/HelpOutline'\nimport {\n DuoCategory,\n DuoTerm,\n duoRegistryUrl,\n resolveDuoTerm,\n} from './duoTerms'\nimport FacetValueChip from '@/components/widgets/facet-nav/FacetValueChip'\nimport type { RenderedFacetValueChip } from '@/components/QueryVisualizationWrapper/QueryVisualizationContext'\n\nexport type DuoTermTagsProps = {\n /** Raw `dataUseModifiers` values (ontology codes or term names). */\n terms: string[]\n}\n\n// An icon per code so the meaning reads at a glance.\nconst DUO_ICONS: Record<string, ComponentType> = {\n 'DUO:0000042': ScienceOutlined, // General Research Use\n 'DUO:0000006': MedicalServicesOutlined, // Health/Medical/Biomedical\n 'DUO:0000007': CoronavirusOutlined, // Disease Specific\n 'DUO:0000011': GroupsOutlined, // Population Origins/Ancestry\n 'DUO:0000043': HealingOutlined, // Clinical Care Use\n 'DUO:0000004': LockOpenOutlined, // No Restriction\n 'DUO:0000046': MoneyOffOutlined, // Non-Commercial Use Only\n 'DUO:0000018': MoneyOffOutlined, // Not-for-Profit, Non-Commercial\n 'DUO:0000045': MoneyOffOutlined, // Not-for-Profit Organisation\n 'DUO:0000019': ArticleOutlined, // Publication Required\n 'DUO:0000020': HandshakeOutlined, // Collaboration Required\n 'DUO:0000021': GavelOutlined, // Ethics Approval Required\n 'DUO:0000029': ReplayOutlined, // Return to Database/Resource\n DUOplus7: FormatQuoteOutlined, // Attribution Required\n 'DUO:0000026': PersonOutlined, // User Specific\n 'DUO:0000027': AssignmentOutlined, // Project Specific\n 'DUO:0000028': ApartmentOutlined, // Institution Specific\n 'DUO:0000022': PublicOutlined, // Geographical Restriction\n 'DUO:0000025': ScheduleOutlined, // Time Limit\n 'DUO:0000024': HourglassEmptyOutlined, // Publication Moratorium\n 'DUO:0000016': GeneticsIcon, // Genetic Studies Only (Material Symbols)\n 'DUO:0000015': BlockOutlined, // No General Methods Research\n 'DUO:0000044': BlockOutlined, // Population/Ancestry Prohibited\n 'DUO:0000012': RuleOutlined, // Research Specific Restrictions\n}\n\nconst TermIcon = (props: { term: DuoTerm }): ReactNode => {\n const { term } = props\n const Icon = DUO_ICONS[term.code] ?? HelpOutline\n return <Icon />\n}\n\n// Primary use first (the dataset's purpose), then the conditions that limit it.\nconst ORDER: Record<DuoCategory, number> = {\n permission: 0,\n commercial: 1,\n obligation: 2,\n limit: 3,\n}\n\n// The rich tooltip for a DUO term: full name, abbreviation, ontology code\n// (linked to the registry when available), and definition.\nfunction duoTooltipTitle(t: DuoTerm): ReactNode {\n const url = duoRegistryUrl(t)\n const hasMeta = Boolean(t.abbr || t.code)\n return (\n <>\n <strong>{t.name}</strong>\n {hasMeta && (\n <>\n <br />\n {t.abbr}\n {t.abbr && t.code ? ' · ' : ''}\n {url ? (\n <Link href={url} target=\"_blank\" rel=\"noopener\" color=\"inherit\">\n {t.code}\n </Link>\n ) : (\n t.code\n )}\n </>\n )}\n {t.definition && t.definition !== t.name && (\n <>\n <br />\n {t.definition}\n </>\n )}\n </>\n )\n}\n\n/** Renders a dataset's DUO data-use modifiers as a row of chips (used on cards). */\nexport default function DuoTermTags(props: DuoTermTagsProps) {\n const { terms } = props\n if (!terms || terms.length === 0) {\n return null\n }\n const sorted = terms\n .map(resolveDuoTerm)\n .sort((a, b) => ORDER[a.category] - ORDER[b.category])\n\n return (\n <Box\n sx={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: 0.75,\n minWidth: 0,\n maxWidth: '100%',\n }}\n >\n {sorted.map((t, i) => (\n <FacetValueChip\n key={i}\n label={t.name}\n icon={<TermIcon term={t} />}\n tooltipTitle={duoTooltipTitle(t)}\n />\n ))}\n </Box>\n )\n}\n\n/**\n * Builds a `renderFacetValue` function (for `QueryVisualizationWrapper`) that\n * returns the chip content for the given DUO column's enumeration facet values\n * — used by both the facet sidebar and the active-filter pills, which render it\n * through the shared `FacetValueChip`. Other columns fall through to text.\n *\n * Portals only need to declare the DUO column (via the card schema's\n * `dataUseModifiersColumnName`) rather than each defining their own handler.\n */\nexport function createDuoFacetValueRenderer(duoColumnName: string) {\n return (\n columnName: string,\n value: string,\n ): RenderedFacetValueChip | undefined => {\n if (columnName !== duoColumnName) {\n return undefined\n }\n const t = resolveDuoTerm(value)\n return {\n label: t.name,\n icon: <TermIcon term={t} />,\n tooltipTitle: duoTooltipTitle(t),\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,IAAM,IAA2C;CAC/C,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,UAAU;CACV,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;CACf,eAAe;AACjB,GAEM,KAAY,MAAwC;CACxD,IAAM,EAAE,YAAS;CAEjB,OAAO,kBADM,EAAU,EAAK,SAAS,GAC9B,CAAO,CAAA;AAChB,GAGM,IAAqC;CACzC,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,OAAO;AACT;AAIA,SAAS,EAAgB,GAAuB;CAC9C,IAAM,IAAM,EAAe,CAAC,GACtB,IAAU,GAAQ,EAAE,QAAQ,EAAE;CACpC,OACE,kBAAA,GAAA,EAAA,UAAA;EACE,kBAAC,UAAD,EAAA,UAAS,EAAE,KAAa,CAAA;EACvB,KACC,kBAAA,GAAA,EAAA,UAAA;GACE,kBAAC,MAAD,CAAK,CAAA;GACJ,EAAE;GACF,EAAE,QAAQ,EAAE,OAAO,QAAQ;GAC3B,IACC,kBAAC,GAAD;IAAM,MAAM;IAAK,QAAO;IAAS,KAAI;IAAW,OAAM;cACnD,EAAE;GACC,CAAA,IAEN,EAAE;EAEJ,EAAA,CAAA;EAEH,EAAE,cAAc,EAAE,eAAe,EAAE,QAClC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,MAAD,CAAK,CAAA,GACJ,EAAE,UACH,EAAA,CAAA;CAEJ,EAAA,CAAA;AAEN;AAGA,SAAwB,EAAY,GAAyB;CAC3D,IAAM,EAAE,aAAU;CAQlB,OAPI,CAAC,KAAS,EAAM,WAAW,IACtB,OAOP,kBAAC,GAAD;EACE,IAAI;GACF,SAAS;GACT,UAAU;GACV,KAAK;GACL,UAAU;GACV,UAAU;EACZ;YAZW,EACZ,IAAI,CAAc,EAClB,MAAM,GAAG,MAAM,EAAM,EAAE,YAAY,EAAM,EAAE,SAYzC,EAAO,KAAK,GAAG,MACd,kBAAC,GAAD;GAEE,OAAO,EAAE;GACT,MAAM,kBAAC,GAAD,EAAU,MAAM,EAAI,CAAA;GAC1B,cAAc,EAAgB,CAAC;EAChC,GAJM,CAIN,CACF;CACE,CAAA;AAET;AAWA,SAAgB,EAA4B,GAAuB;CACjE,QACE,GACA,MACuC;EACvC,IAAI,MAAe,GACjB;EAEF,IAAM,IAAI,EAAe,CAAK;EAC9B,OAAO;GACL,OAAO,EAAE;GACT,MAAM,kBAAC,GAAD,EAAU,MAAM,EAAI,CAAA;GAC1B,cAAc,EAAgB,CAAC;EACjC;CACF;AACF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type DuoCategory = 'permission' | 'commercial' | 'obligation' | 'limit';
|
|
2
|
+
export type DuoTerm = {
|
|
3
|
+
/** Ontology code, e.g. "DUO:0000042". DUO-Plus terms use a "DUOplus#" code. */
|
|
4
|
+
code: string;
|
|
5
|
+
/** Short abbreviation (shown in tooltips). */
|
|
6
|
+
abbr: string;
|
|
7
|
+
/** Full human-readable name (the visible tag label). */
|
|
8
|
+
name: string;
|
|
9
|
+
category: DuoCategory;
|
|
10
|
+
definition: string;
|
|
11
|
+
};
|
|
12
|
+
/** All known terms, in canonical (category) order — for demos/legends. */
|
|
13
|
+
export declare const ALL_DUO_TERMS: readonly DuoTerm[];
|
|
14
|
+
/**
|
|
15
|
+
* Link to the term's definition in Bioregistry (GA4GH DUO terms only;
|
|
16
|
+
* DUO-Plus extensions have no registry entry).
|
|
17
|
+
*/
|
|
18
|
+
export declare function duoRegistryUrl(term: DuoTerm): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Parse a `dataUseModifiers` cell into a list of values. The column is a
|
|
21
|
+
* STRING_LIST, returned as a JSON-array string (e.g. `["General Research Use"]`
|
|
22
|
+
* or, once migrated, `["DUO:0000042"]`).
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseDuoModifiers(raw: string | null | undefined): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a raw `dataUseModifiers` value (an ontology code like DUO:0000042, a
|
|
27
|
+
* DUOplus# code, or a term name) to a known DuoTerm. Unknown values fall back
|
|
28
|
+
* to a neutral "limit" tag built from the raw text so nothing is dropped.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveDuoTerm(raw: string): DuoTerm;
|
|
31
|
+
//# sourceMappingURL=duoTerms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duoTerms.d.ts","sourceRoot":"","sources":["../../../../src/components/GenericCard/DuoTermTags/duoTerms.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,OAAO,CAAA;AAE9E,MAAM,MAAM,OAAO,GAAG;IACpB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAA;IACZ,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,WAAW,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAiMD,0EAA0E;AAC1E,eAAO,MAAM,aAAa,EAAE,SAAS,OAAO,EAAU,CAAA;AAEtD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIhE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAU1E;AAWD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAoBnD"}
|