synapse-react-client 4.0.43 → 4.0.45
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 -1
- package/dist/SWC.index.js +2 -2
- package/dist/SWC.index.js.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementList.d.ts +2 -1
- package/dist/components/AccessRequirementList/AccessRequirementList.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementList.js +126 -125
- package/dist/components/AccessRequirementList/AccessRequirementList.js.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementListItem.d.ts +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementListItem.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementListItem.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.js +28 -16
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.js +99 -97
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/requestFlowTypes.d.ts +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/requestFlowTypes.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/RequestDataStep.d.ts +1 -4
- package/dist/components/AccessRequirementList/RequestDataStep.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/RequestDataStep.js +15 -18
- package/dist/components/AccessRequirementList/RequestDataStep.js.map +1 -1
- package/dist/components/CreateOrUpdateAccessRequirementWizard/CreateOrUpdateAccessRequirementWizard.d.ts.map +1 -1
- package/dist/components/CreateOrUpdateAccessRequirementWizard/CreateOrUpdateAccessRequirementWizard.js +52 -49
- package/dist/components/CreateOrUpdateAccessRequirementWizard/CreateOrUpdateAccessRequirementWizard.js.map +1 -1
- package/dist/components/DataGrid/DataGridWebSocket.d.ts +29 -1
- package/dist/components/DataGrid/DataGridWebSocket.d.ts.map +1 -1
- package/dist/components/DataGrid/DataGridWebSocket.js +48 -16
- package/dist/components/DataGrid/DataGridWebSocket.js.map +1 -1
- package/dist/components/DataGrid/components/GridLoadingState.d.ts +2 -2
- package/dist/components/DataGrid/components/GridLoadingState.d.ts.map +1 -1
- package/dist/components/DataGrid/components/GridLoadingState.js +2 -2
- package/dist/components/DataGrid/components/GridLoadingState.js.map +1 -1
- package/dist/components/DataGrid/useDataGridWebsocket.d.ts.map +1 -1
- package/dist/components/DataGrid/useDataGridWebsocket.js +2 -1
- package/dist/components/DataGrid/useDataGridWebsocket.js.map +1 -1
- package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.d.ts.map +1 -1
- package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.js +11 -10
- package/dist/components/EntityViewScopeEditor/EntityViewMaskEditor.js.map +1 -1
- package/dist/components/FormTemplateEditor/useSaveFormTemplate.d.ts.map +1 -1
- package/dist/components/FormTemplateEditor/useSaveFormTemplate.js.map +1 -1
- package/dist/components/FormTemplateReferenceField/FormTemplatePreviewDialog.d.ts +9 -0
- package/dist/components/FormTemplateReferenceField/FormTemplatePreviewDialog.d.ts.map +1 -0
- package/dist/components/FormTemplateReferenceField/FormTemplatePreviewDialog.js +45 -0
- package/dist/components/FormTemplateReferenceField/FormTemplatePreviewDialog.js.map +1 -0
- package/dist/components/FormTemplateReferenceField/FormTemplateReferenceField.d.ts +22 -0
- package/dist/components/FormTemplateReferenceField/FormTemplateReferenceField.d.ts.map +1 -0
- package/dist/components/FormTemplateReferenceField/FormTemplateReferenceField.js +139 -0
- package/dist/components/FormTemplateReferenceField/FormTemplateReferenceField.js.map +1 -0
- package/dist/components/Forum/ForumTable.d.ts +17 -5
- package/dist/components/Forum/ForumTable.d.ts.map +1 -1
- package/dist/components/Forum/ForumTable.js +94 -77
- package/dist/components/Forum/ForumTable.js.map +1 -1
- package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.d.ts.map +1 -1
- package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.js +142 -108
- package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.js.map +1 -1
- package/dist/components/SetBasicAccessRequirementFields/AccessorRequirements.d.ts.map +1 -1
- package/dist/components/SetBasicAccessRequirementFields/AccessorRequirements.js +15 -15
- package/dist/components/SetBasicAccessRequirementFields/AccessorRequirements.js.map +1 -1
- package/dist/components/SetManagedAccessRequirementFields/DataUseCertificateFields.d.ts +10 -0
- package/dist/components/SetManagedAccessRequirementFields/DataUseCertificateFields.d.ts.map +1 -0
- package/dist/components/SetManagedAccessRequirementFields/DataUseCertificateFields.js +81 -0
- package/dist/components/SetManagedAccessRequirementFields/DataUseCertificateFields.js.map +1 -0
- package/dist/components/SetManagedAccessRequirementFields/ExpirationPeriodField.d.ts +9 -0
- package/dist/components/SetManagedAccessRequirementFields/ExpirationPeriodField.d.ts.map +1 -0
- package/dist/components/SetManagedAccessRequirementFields/ExpirationPeriodField.js +32 -0
- package/dist/components/SetManagedAccessRequirementFields/ExpirationPeriodField.js.map +1 -0
- package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.d.ts +0 -2
- package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.d.ts.map +1 -1
- package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js +79 -154
- package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js.map +1 -1
- package/dist/components/TanStackTable/TanStackTableUtils.d.ts.map +1 -1
- package/dist/components/TanStackTable/TanStackTableUtils.js +8 -5
- package/dist/components/TanStackTable/TanStackTableUtils.js.map +1 -1
- package/dist/components/dataaccess/SubmissionPage/DataAccessSubmissionFileHandleLink.d.ts +5 -0
- package/dist/components/dataaccess/SubmissionPage/DataAccessSubmissionFileHandleLink.d.ts.map +1 -0
- package/dist/components/dataaccess/SubmissionPage/DataAccessSubmissionFileHandleLink.js +20 -0
- package/dist/components/dataaccess/SubmissionPage/DataAccessSubmissionFileHandleLink.js.map +1 -0
- package/dist/components/dataaccess/SubmissionPage/SubmissionPage.d.ts.map +1 -1
- package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js +183 -188
- package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js.map +1 -1
- package/dist/components/dataaccess/SubmissionPage/SubmissionSchemaAnswers.d.ts +25 -0
- package/dist/components/dataaccess/SubmissionPage/SubmissionSchemaAnswers.d.ts.map +1 -0
- package/dist/components/dataaccess/SubmissionPage/SubmissionSchemaAnswers.js +125 -0
- package/dist/components/dataaccess/SubmissionPage/SubmissionSchemaAnswers.js.map +1 -0
- package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.d.ts.map +1 -1
- package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.js +4 -1
- package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.js.map +1 -1
- package/dist/mocks/accessRequirement/mockJsonSchemaAccessRequirements.d.ts +5 -0
- package/dist/mocks/accessRequirement/mockJsonSchemaAccessRequirements.d.ts.map +1 -1
- package/dist/mocks/accessRequirement/mockJsonSchemaAccessRequirements.js +37 -26
- package/dist/mocks/accessRequirement/mockJsonSchemaAccessRequirements.js.map +1 -1
- package/dist/mocks/dataaccess/MockSubmission.d.ts +76 -0
- package/dist/mocks/dataaccess/MockSubmission.d.ts.map +1 -1
- package/dist/mocks/dataaccess/MockSubmission.js +108 -63
- package/dist/mocks/dataaccess/MockSubmission.js.map +1 -1
- package/dist/mocks/msw/handlers/discussionHandlers.d.ts.map +1 -1
- package/dist/mocks/msw/handlers/discussionHandlers.js +40 -33
- package/dist/mocks/msw/handlers/discussionHandlers.js.map +1 -1
- package/dist/style/components/_submission-page.scss +0 -4
- package/dist/style/main.css +1 -1
- package/dist/synapse-client/SynapseClient.d.ts +10 -0
- package/dist/synapse-client/SynapseClient.d.ts.map +1 -1
- package/dist/synapse-client/SynapseClient.js +486 -482
- package/dist/synapse-client/SynapseClient.js.map +1 -1
- package/dist/synapse-client/index.js +3 -3
- package/dist/synapse-queries/KeyFactory.d.ts +13 -1
- package/dist/synapse-queries/KeyFactory.d.ts.map +1 -1
- package/dist/synapse-queries/KeyFactory.js +11 -1
- package/dist/synapse-queries/KeyFactory.js.map +1 -1
- package/dist/synapse-queries/dataaccess/index.js +4 -4
- package/dist/synapse-queries/dataaccess/useAccessRequirements.d.ts.map +1 -1
- package/dist/synapse-queries/dataaccess/useAccessRequirements.js +4 -1
- package/dist/synapse-queries/dataaccess/useAccessRequirements.js.map +1 -1
- package/dist/synapse-queries/dataaccess/useEDuc.d.ts +19 -5
- package/dist/synapse-queries/dataaccess/useEDuc.d.ts.map +1 -1
- package/dist/synapse-queries/dataaccess/useEDuc.js +29 -21
- package/dist/synapse-queries/dataaccess/useEDuc.js.map +1 -1
- package/dist/synapse-queries/dataaccess/useFormTemplate.d.ts +12 -0
- package/dist/synapse-queries/dataaccess/useFormTemplate.d.ts.map +1 -1
- package/dist/synapse-queries/dataaccess/useFormTemplate.js +52 -30
- package/dist/synapse-queries/dataaccess/useFormTemplate.js.map +1 -1
- package/dist/synapse-queries/dataaccess/useGeneratedSubmissionForm.d.ts +37 -0
- package/dist/synapse-queries/dataaccess/useGeneratedSubmissionForm.d.ts.map +1 -0
- package/dist/synapse-queries/dataaccess/useGeneratedSubmissionForm.js +35 -0
- package/dist/synapse-queries/dataaccess/useGeneratedSubmissionForm.js.map +1 -0
- package/dist/synapse-queries/forum/useForum.d.ts +5 -1
- package/dist/synapse-queries/forum/useForum.d.ts.map +1 -1
- package/dist/synapse-queries/forum/useForum.js +23 -1
- package/dist/synapse-queries/forum/useForum.js.map +1 -1
- package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts +2 -0
- package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts.map +1 -1
- package/dist/synapse-queries/grid/useEstablishWebsocketConnection.js.map +1 -1
- package/dist/synapse-queries/index.js +185 -184
- package/dist/synapse-queries/jsonschema/useCreateJsonSchema.d.ts +5 -3
- package/dist/synapse-queries/jsonschema/useCreateJsonSchema.d.ts.map +1 -1
- package/dist/synapse-queries/jsonschema/useCreateJsonSchema.js +12 -15
- package/dist/synapse-queries/jsonschema/useCreateJsonSchema.js.map +1 -1
- package/dist/synapse-queries/termsOfService/useTermsOfService.d.ts.map +1 -1
- package/dist/synapse-queries/termsOfService/useTermsOfService.js +21 -19
- package/dist/synapse-queries/termsOfService/useTermsOfService.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/featureflag/FeatureFlags.d.ts.map +1 -1
- package/dist/utils/featureflag/FeatureFlags.js.map +1 -1
- package/dist/utils/functions/EntityTypeUtils.d.ts +2 -2
- package/dist/utils/functions/EntityTypeUtils.d.ts.map +1 -1
- package/dist/utils/functions/EntityTypeUtils.js +135 -135
- package/dist/utils/functions/EntityTypeUtils.js.map +1 -1
- package/dist/utils/functions/getEndpoint.d.ts +1 -0
- package/dist/utils/functions/getEndpoint.d.ts.map +1 -1
- package/dist/utils/functions/getEndpoint.js +3 -3
- package/dist/utils/functions/getEndpoint.js.map +1 -1
- package/dist/utils/types/RequestableAccessRequirement.d.ts +8 -0
- package/dist/utils/types/RequestableAccessRequirement.d.ts.map +1 -0
- package/dist/utils/types/RequestableAccessRequirement.js +9 -0
- package/dist/utils/types/RequestableAccessRequirement.js.map +1 -0
- package/package.json +3 -3
- package/dist/testutils/ReactQueryMockUtils.js +0 -676
- package/dist/testutils/ReactQueryMockUtils.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDataGridWebsocket.js","names":["GridModel","GridModelSnapshot","useCRDTModelView","normalizeWebsocketError","useCallback","useEffect","useMemo","useReducer","useRef","DataGridWebSocket","useEstablishWebsocketConnection","useDocumentVisibility","WebSocketState","model","hasCompletedInitialSync","hasCompletedInitialLoad","isSyncing","isConnected","isConnecting","connectionParams","replicaId","sessionId","websocketInstance","connectionAttemptId","connectionError","websocketError","WebSocketAction","type","payload","attemptId","websocketReducer","state","action","isSameConnection","initialWebSocketState","isModelRenderable","modelSnapshot","api","getSnapshot","columnNames","columnOrder","rows","columnsReady","length","orderReady","rowsReady","isInitialLoadReady","UseDataGridWebSocketOptions","onGridReady","onReplicaConnected","onReplicaDisconnected","useDataGridWebSocket","options","dispatch","connectionAttemptCounter","activeConnectionAttemptIdRef","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"],"sources":["../../../src/components/DataGrid/useDataGridWebsocket.ts"],"sourcesContent":["import {\n GridModel,\n GridModelSnapshot,\n} 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\nexport interface WebSocketState {\n model: GridModel | null\n /**\n * True if the WebSocket has finished a sync exchange on the current connection\n * and can process CRDT updates. Set by `GRID_READY`; cleared on disconnect until\n * `hasCompletedInitialLoad` is set, since a drop mid-replay leaves the model\n * holding only part of the server's data.\n */\n hasCompletedInitialSync: boolean\n /**\n * True once the first connection for the current session has finished replaying\n * the server's data into the model. A one-way transition: later sync activity\n * never takes it back to false, and only connecting to a different\n * session/replica resets it.\n */\n hasCompletedInitialLoad: 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\nexport type 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: 'INITIAL_LOAD_COMPLETE' }\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\nexport function 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 hasCompletedInitialLoad: isSameConnection\n ? state.hasCompletedInitialLoad\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 // A drop before the initial load finishes invalidates the completed-exchange\n // signal: the model may hold only the part of the data that arrived so far.\n // Clearing it makes the readiness gate wait for the reconnect's replay\n // instead of latching onto a partially loaded model. Once the load is done,\n // drops are ordinary reconnects and the signal must survive them.\n hasCompletedInitialSync: state.hasCompletedInitialLoad\n ? state.hasCompletedInitialSync\n : false,\n }\n\n case 'GRID_READY':\n return {\n ...state,\n hasCompletedInitialSync: true,\n }\n\n case 'INITIAL_LOAD_COMPLETE':\n return {\n ...state,\n hasCompletedInitialLoad: 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 hasCompletedInitialLoad: 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 * Checks if the model snapshot contains the minimum data required for rendering (columns and rows).\n */\nfunction isModelRenderable(\n model: GridModel | null,\n modelSnapshot: GridModelSnapshot | null | undefined,\n) {\n if (!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/**\n * Whether the server has finished replaying its data into the model, so the grid\n * can be shown and edited. Drives the one-way `INITIAL_LOAD_COMPLETE` transition.\n *\n * Returns false once `hasCompletedInitialLoad` is set, so callers can dispatch\n * unconditionally on a true result without re-entering the transition.\n *\n * `hasCompletedInitialSync` alone is not sufficient: the server may complete the\n * snapshot request before the client has fetched and decoded the snapshot, which\n * leaves that flag true while the rows are still arriving. Requiring an idle sync\n * exchange holds the transition until the replay drains.\n */\nexport function isInitialLoadReady(\n state: WebSocketState,\n modelSnapshot: GridModelSnapshot | null | undefined,\n): boolean {\n return (\n !state.hasCompletedInitialLoad &&\n state.hasCompletedInitialSync &&\n !state.isSyncing &&\n isModelRenderable(state.model, modelSnapshot)\n )\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 useEffect(() => {\n if (isInitialLoadReady(state, modelSnapshot)) {\n dispatch({ type: 'INITIAL_LOAD_COMPLETE' })\n }\n }, [state, modelSnapshot])\n\n return {\n isConnected: state.isConnected,\n websocketInstance: state.websocketInstance,\n hasCompletedInitialSync: state.hasCompletedInitialSync,\n hasCompletedInitialLoad: state.hasCompletedInitialLoad,\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 }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useDataGridWebsocket.js","names":["GridModel","GridModelSnapshot","useCRDTModelView","normalizeWebsocketError","useCallback","useEffect","useMemo","useReducer","useRef","DataGridWebSocket","useEstablishWebsocketConnection","useDocumentVisibility","WebSocketState","model","hasCompletedInitialSync","hasCompletedInitialLoad","isSyncing","isConnected","isConnecting","connectionParams","replicaId","sessionId","websocketInstance","connectionAttemptId","connectionError","websocketError","WebSocketAction","type","payload","attemptId","websocketReducer","state","action","isSameConnection","initialWebSocketState","isModelRenderable","modelSnapshot","api","getSnapshot","columnNames","columnOrder","rows","columnsReady","length","orderReady","rowsReady","isInitialLoadReady","UseDataGridWebSocketOptions","onGridReady","onReplicaConnected","onReplicaDisconnected","useDataGridWebSocket","options","dispatch","connectionAttemptCounter","activeConnectionAttemptIdRef","isDocumentVisible","mutateAsync","establishWebsocketConnection","isPending","isEstablishingWebsocketConnection","error","errorEstablishingWebsocketConnection","presignedUrl","reset","resetEstablishWebsocketConnection","clearPresignedUrl","handleModelCreate","newModel","websocketOptionsWithoutModel","onStatusChange","open","onModelCreate","onSyncStart","onSyncEnd","onError","connect","isDifferentConnection","current","websocketOptions","pendingPatches","getPendingPatches","undefined","then","ws","disconnect","catch","err","console","previousConnectionParams","previous"],"sources":["../../../src/components/DataGrid/useDataGridWebsocket.ts"],"sourcesContent":["import {\n GridModel,\n GridModelSnapshot,\n} 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\nexport interface WebSocketState {\n model: GridModel | null\n /**\n * True if the WebSocket has finished a sync exchange on the current connection\n * and can process CRDT updates. Set by `GRID_READY`; cleared on disconnect until\n * `hasCompletedInitialLoad` is set, since a drop mid-replay leaves the model\n * holding only part of the server's data.\n */\n hasCompletedInitialSync: boolean\n /**\n * True once the first connection for the current session has finished replaying\n * the server's data into the model. A one-way transition: later sync activity\n * never takes it back to false, and only connecting to a different\n * session/replica resets it.\n */\n hasCompletedInitialLoad: 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\nexport type 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: 'INITIAL_LOAD_COMPLETE' }\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\nexport function 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 hasCompletedInitialLoad: isSameConnection\n ? state.hasCompletedInitialLoad\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 // A drop before the initial load finishes invalidates the completed-exchange\n // signal: the model may hold only the part of the data that arrived so far.\n // Clearing it makes the readiness gate wait for the reconnect's replay\n // instead of latching onto a partially loaded model. Once the load is done,\n // drops are ordinary reconnects and the signal must survive them.\n hasCompletedInitialSync: state.hasCompletedInitialLoad\n ? state.hasCompletedInitialSync\n : false,\n }\n\n case 'GRID_READY':\n return {\n ...state,\n hasCompletedInitialSync: true,\n }\n\n case 'INITIAL_LOAD_COMPLETE':\n return {\n ...state,\n hasCompletedInitialLoad: 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 hasCompletedInitialLoad: 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 * Checks if the model snapshot contains the minimum data required for rendering (columns and rows).\n */\nfunction isModelRenderable(\n model: GridModel | null,\n modelSnapshot: GridModelSnapshot | null | undefined,\n) {\n if (!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/**\n * Whether the server has finished replaying its data into the model, so the grid\n * can be shown and edited. Drives the one-way `INITIAL_LOAD_COMPLETE` transition.\n *\n * Returns false once `hasCompletedInitialLoad` is set, so callers can dispatch\n * unconditionally on a true result without re-entering the transition.\n *\n * `hasCompletedInitialSync` alone is not sufficient: the server may complete the\n * snapshot request before the client has fetched and decoded the snapshot, which\n * leaves that flag true while the rows are still arriving. Requiring an idle sync\n * exchange holds the transition until the replay drains.\n */\nexport function isInitialLoadReady(\n state: WebSocketState,\n modelSnapshot: GridModelSnapshot | null | undefined,\n): boolean {\n return (\n !state.hasCompletedInitialLoad &&\n state.hasCompletedInitialSync &&\n !state.isSyncing &&\n isModelRenderable(state.model, modelSnapshot)\n )\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 // A model only survives for the same session, so patches only carry over with it\n pendingPatches: state.model\n ? state.websocketInstance?.getPendingPatches()\n : undefined,\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 useEffect(() => {\n if (isInitialLoadReady(state, modelSnapshot)) {\n dispatch({ type: 'INITIAL_LOAD_COMPLETE' })\n }\n }, [state, modelSnapshot])\n\n return {\n isConnected: state.isConnected,\n websocketInstance: state.websocketInstance,\n hasCompletedInitialSync: state.hasCompletedInitialSync,\n hasCompletedInitialLoad: state.hasCompletedInitialLoad,\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 }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityViewMaskEditor.d.ts","sourceRoot":"","sources":["../../../src/components/EntityViewScopeEditor/EntityViewMaskEditor.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EntityViewMaskEditor.d.ts","sourceRoot":"","sources":["../../../src/components/EntityViewScopeEditor/EntityViewMaskEditor.tsx"],"names":[],"mappings":"AAgBA,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAWD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,WAShD;AAED,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,2CA0C5E"}
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { Alert as e, Checkbox as t, FormControlLabel as n, FormGroup as r, Typography as i } from "@mui/material";
|
|
2
2
|
import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
3
|
-
import { ENTITY_VIEW_TYPE_MASK_DATASET as c, ENTITY_VIEW_TYPE_MASK_DOCKER as l, ENTITY_VIEW_TYPE_MASK_FILE as u, ENTITY_VIEW_TYPE_MASK_FOLDER as d,
|
|
3
|
+
import { ENTITY_VIEW_TYPE_MASK_DATASET as c, ENTITY_VIEW_TYPE_MASK_DOCKER as l, ENTITY_VIEW_TYPE_MASK_FILE as u, ENTITY_VIEW_TYPE_MASK_FOLDER as d, ENTITY_VIEW_TYPE_MASK_RECORDSET as f, ENTITY_VIEW_TYPE_MASK_TABLE as p } from "@sage-bionetworks/synapse-types";
|
|
4
4
|
//#region src/components/EntityViewScopeEditor/EntityViewMaskEditor.tsx
|
|
5
|
-
var
|
|
5
|
+
var m = [
|
|
6
6
|
["Files", u],
|
|
7
7
|
["Folders", d],
|
|
8
|
-
["Tables",
|
|
8
|
+
["Tables", p],
|
|
9
9
|
["Datasets", c],
|
|
10
|
-
["Docker Repositories", l]
|
|
10
|
+
["Docker Repositories", l],
|
|
11
|
+
["Record Sets", f]
|
|
11
12
|
];
|
|
12
|
-
function
|
|
13
|
-
return
|
|
13
|
+
function h(e) {
|
|
14
|
+
return m.forEach(([, t]) => {
|
|
14
15
|
(e & t) !== 0 && (e -= t);
|
|
15
16
|
}), e === 0;
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
-
let { value: l, onChange: u, disabled: d = !1 } = c, f = !
|
|
18
|
+
function g(c) {
|
|
19
|
+
let { value: l, onChange: u, disabled: d = !1 } = c, f = !h(l);
|
|
19
20
|
return /* @__PURE__ */ s(a, { children: [
|
|
20
21
|
/* @__PURE__ */ o(i, {
|
|
21
22
|
variant: "body1",
|
|
@@ -28,7 +29,7 @@ function h(c) {
|
|
|
28
29
|
}),
|
|
29
30
|
/* @__PURE__ */ o(r, {
|
|
30
31
|
sx: { gap: 1 },
|
|
31
|
-
children:
|
|
32
|
+
children: m.map(([e, r]) => /* @__PURE__ */ o(n, {
|
|
32
33
|
control: /* @__PURE__ */ o(t, {}),
|
|
33
34
|
label: e,
|
|
34
35
|
checked: (l & r) > 0,
|
|
@@ -46,6 +47,6 @@ function h(c) {
|
|
|
46
47
|
] });
|
|
47
48
|
}
|
|
48
49
|
//#endregion
|
|
49
|
-
export {
|
|
50
|
+
export { g as default, h as isMaskSupportedInUI };
|
|
50
51
|
|
|
51
52
|
//# sourceMappingURL=EntityViewMaskEditor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityViewMaskEditor.js","names":[],"sources":["../../../src/components/EntityViewScopeEditor/EntityViewMaskEditor.tsx"],"sourcesContent":["import {\n Alert,\n Checkbox,\n FormControlLabel,\n FormGroup,\n Typography,\n} from '@mui/material'\nimport {\n ENTITY_VIEW_TYPE_MASK_DATASET,\n ENTITY_VIEW_TYPE_MASK_FILE,\n ENTITY_VIEW_TYPE_MASK_FOLDER,\n ENTITY_VIEW_TYPE_MASK_TABLE,\n ENTITY_VIEW_TYPE_MASK_DOCKER,\n} from '@sage-bionetworks/synapse-types'\n\nexport type EntityViewMaskEditorProps = {\n value: number\n onChange: (mask: number) => void\n disabled?: boolean\n}\n\nconst maskLabelPairs: [string, number][] = [\n ['Files', ENTITY_VIEW_TYPE_MASK_FILE],\n ['Folders', ENTITY_VIEW_TYPE_MASK_FOLDER],\n ['Tables', ENTITY_VIEW_TYPE_MASK_TABLE],\n ['Datasets', ENTITY_VIEW_TYPE_MASK_DATASET],\n ['Docker Repositories', ENTITY_VIEW_TYPE_MASK_DOCKER],\n]\n\nexport function isMaskSupportedInUI(value: number) {\n maskLabelPairs.forEach(([, mask]) => {\n // Remove each supported bit\n if ((value & mask) !== 0) {\n value = value - mask\n }\n })\n // If value is not zero, then some unsupported bit remains\n return value === 0\n}\n\nexport default function EntityViewMaskEditor(props: EntityViewMaskEditorProps) {\n const { value, onChange, disabled = false } = props\n\n const customMaskInUse = !isMaskSupportedInUI(value)\n\n return (\n <>\n <Typography\n variant={'body1'}\n sx={{\n mt: 2.5,\n mb: 1.25,\n fontWeight: 700,\n }}\n >\n Include in View\n </Typography>\n <FormGroup sx={{ gap: 1 }}>\n {maskLabelPairs.map(([label, mask]) => (\n <FormControlLabel\n control={<Checkbox />}\n key={label}\n label={label}\n checked={\n // Checked if the mask bit is set\n (value & mask) > 0\n }\n disabled={customMaskInUse || disabled}\n onChange={() => {\n // Toggle the clicked mask value with XOR\n onChange(value ^ mask)\n }}\n />\n ))}\n </FormGroup>\n {customMaskInUse && (\n <Alert severity={'warning'} sx={{ my: 2.25 }}>\n A custom mask is in use. Changes cannot be made in the UI.\n </Alert>\n )}\n </>\n )\n}\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"EntityViewMaskEditor.js","names":[],"sources":["../../../src/components/EntityViewScopeEditor/EntityViewMaskEditor.tsx"],"sourcesContent":["import {\n Alert,\n Checkbox,\n FormControlLabel,\n FormGroup,\n Typography,\n} from '@mui/material'\nimport {\n ENTITY_VIEW_TYPE_MASK_DATASET,\n ENTITY_VIEW_TYPE_MASK_FILE,\n ENTITY_VIEW_TYPE_MASK_FOLDER,\n ENTITY_VIEW_TYPE_MASK_TABLE,\n ENTITY_VIEW_TYPE_MASK_DOCKER,\n ENTITY_VIEW_TYPE_MASK_RECORDSET,\n} from '@sage-bionetworks/synapse-types'\n\nexport type EntityViewMaskEditorProps = {\n value: number\n onChange: (mask: number) => void\n disabled?: boolean\n}\n\nconst maskLabelPairs: [string, number][] = [\n ['Files', ENTITY_VIEW_TYPE_MASK_FILE],\n ['Folders', ENTITY_VIEW_TYPE_MASK_FOLDER],\n ['Tables', ENTITY_VIEW_TYPE_MASK_TABLE],\n ['Datasets', ENTITY_VIEW_TYPE_MASK_DATASET],\n ['Docker Repositories', ENTITY_VIEW_TYPE_MASK_DOCKER],\n ['Record Sets', ENTITY_VIEW_TYPE_MASK_RECORDSET],\n]\n\nexport function isMaskSupportedInUI(value: number) {\n maskLabelPairs.forEach(([, mask]) => {\n // Remove each supported bit\n if ((value & mask) !== 0) {\n value = value - mask\n }\n })\n // If value is not zero, then some unsupported bit remains\n return value === 0\n}\n\nexport default function EntityViewMaskEditor(props: EntityViewMaskEditorProps) {\n const { value, onChange, disabled = false } = props\n\n const customMaskInUse = !isMaskSupportedInUI(value)\n\n return (\n <>\n <Typography\n variant={'body1'}\n sx={{\n mt: 2.5,\n mb: 1.25,\n fontWeight: 700,\n }}\n >\n Include in View\n </Typography>\n <FormGroup sx={{ gap: 1 }}>\n {maskLabelPairs.map(([label, mask]) => (\n <FormControlLabel\n control={<Checkbox />}\n key={label}\n label={label}\n checked={\n // Checked if the mask bit is set\n (value & mask) > 0\n }\n disabled={customMaskInUse || disabled}\n onChange={() => {\n // Toggle the clicked mask value with XOR\n onChange(value ^ mask)\n }}\n />\n ))}\n </FormGroup>\n {customMaskInUse && (\n <Alert severity={'warning'} sx={{ my: 2.25 }}>\n A custom mask is in use. Changes cannot be made in the UI.\n </Alert>\n )}\n </>\n )\n}\n"],"mappings":";;;;AAsBA,IAAM,IAAqC;CACzC,CAAC,SAAS,CAA0B;CACpC,CAAC,WAAW,CAA4B;CACxC,CAAC,UAAU,CAA2B;CACtC,CAAC,YAAY,CAA6B;CAC1C,CAAC,uBAAuB,CAA4B;CACpD,CAAC,eAAe,CAA+B;AACjD;AAEA,SAAgB,EAAoB,GAAe;CAQjD,OAPA,EAAe,SAAS,GAAG,OAAU;EAEnC,CAAK,IAAQ,OAAU,MACrB,KAAgB;CAEpB,CAAC,GAEM,MAAU;AACnB;AAEA,SAAwB,EAAqB,GAAkC;CAC7E,IAAM,EAAE,UAAO,aAAU,cAAW,OAAU,GAExC,IAAkB,CAAC,EAAoB,CAAK;CAElD,OACE,kBAAA,GAAA,EAAA,UAAA;EACE,kBAAC,GAAD;GACE,SAAS;GACT,IAAI;IACF,IAAI;IACJ,IAAI;IACJ,YAAY;GACd;aACD;EAEW,CAAA;EACZ,kBAAC,GAAD;GAAW,IAAI,EAAE,KAAK,EAAE;aACrB,EAAe,KAAK,CAAC,GAAO,OAC3B,kBAAC,GAAD;IACE,SAAS,kBAAC,GAAD,CAAW,CAAA;IAEb;IACP,UAEG,IAAQ,KAAQ;IAEnB,UAAU,KAAmB;IAC7B,gBAAgB;KAEd,EAAS,IAAQ,CAAI;IACvB;GACD,GAXM,CAWN,CACF;EACQ,CAAA;EACV,KACC,kBAAC,GAAD;GAAO,UAAU;GAAW,IAAI,EAAE,IAAI,KAAK;aAAG;EAEvC,CAAA;CAET,EAAA,CAAA;AAEN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSaveFormTemplate.d.ts","sourceRoot":"","sources":["../../../src/components/FormTemplateEditor/useSaveFormTemplate.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"useSaveFormTemplate.d.ts","sourceRoot":"","sources":["../../../src/components/FormTemplateEditor/useSaveFormTemplate.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAA;AAC7F,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,OAAO,EAEL,kBAAkB,EAEnB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,wBAAwB,EAAuB,MAAM,SAAS,CAAA;AAMvE,OAAO,EACL,gCAAgC,EAEjC,MAAM,0BAA0B,CAAA;AAEjC,MAAM,MAAM,2BAA2B,GAAG;IACxC,eAAe,CAAC,EAAE,YAAY,CAAA;IAC9B,iBAAiB,CAAC,EAAE,UAAU,CAAA;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,UAAU,CAAA;IACtB,KAAK,EAAE,wBAAwB,EAAE,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,2BAA2B,GACnC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,GACpC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,gBAAgB,EAAE,gCAAgC,EAAE,CAAA;CAAE,CAAA;AAEvE;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,CAAC,EAAE,IAAI,CACZ,kBAAkB,CAChB,2BAA2B,EAC3B,kBAAkB,EAClB,2BAA2B,CAC5B,EACD,YAAY,CACb,4IA2EF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSaveFormTemplate.js","names":[],"sources":["../../../src/components/FormTemplateEditor/useSaveFormTemplate.ts"],"sourcesContent":["/*\n * Network orchestration for saving a FormTemplate draft: pre-flight client-side validation\n * (mirroring the server's per-field rules, since the server only returns a flat message),\n * registering a new JSON Schema version when the schema body changed, and creating or updating\n * the FormTemplate against that schema version.\n */\nimport { getJsonSchemaVersionsQuery } from '@/synapse-queries/jsonschema/useListJsonSchemaVersions'\nimport { useCreateJsonSchema } from '@/synapse-queries/jsonschema/useCreateJsonSchema'\nimport {\n useCreateFormTemplate,\n useUpdateFormTemplate,\n} from '@/synapse-queries/dataaccess/useFormTemplate'\nimport { FormTemplate
|
|
1
|
+
{"version":3,"file":"useSaveFormTemplate.js","names":[],"sources":["../../../src/components/FormTemplateEditor/useSaveFormTemplate.ts"],"sourcesContent":["/*\n * Network orchestration for saving a FormTemplate draft: pre-flight client-side validation\n * (mirroring the server's per-field rules, since the server only returns a flat message),\n * registering a new JSON Schema version when the schema body changed, and creating or updating\n * the FormTemplate against that schema version.\n */\nimport { getJsonSchemaVersionsQuery } from '@/synapse-queries/jsonschema/useListJsonSchemaVersions'\nimport { useCreateJsonSchema } from '@/synapse-queries/jsonschema/useCreateJsonSchema'\nimport {\n useCreateFormTemplate,\n useUpdateFormTemplate,\n} from '@/synapse-queries/dataaccess/useFormTemplate'\nimport { FormTemplate } from '@sage-bionetworks/synapse-client'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport { RJSFSchema } from '@rjsf/utils'\nimport isEqual from 'lodash-es/isEqual'\nimport {\n useMutation,\n UseMutationOptions,\n useQueryClient,\n} from '@tanstack/react-query'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { EditableFormTemplateStep, toFormTemplateSteps } from './utils'\nimport {\n formatSchema$id,\n getNextSchemaVersion,\n resolveFormTemplateSchemaName,\n} from './formTemplateSchema'\nimport {\n FormTemplateFieldValidationError,\n validateFormTemplateFields,\n} from './formTemplateValidation'\n\nexport type SaveFormTemplateDraftParams = {\n initialTemplate?: FormTemplate\n initialJsonSchema?: RJSFSchema\n name: string\n jsonSchema: RJSFSchema\n steps: EditableFormTemplateStep[]\n}\n\nexport type SaveFormTemplateDraftResult =\n | { ok: true; template: FormTemplate }\n | { ok: false; validationErrors: FormTemplateFieldValidationError[] }\n\n/**\n * Owns the create-schema, create-template, and update-template mutations, and sequences them\n * into a single save mutation. A validation failure resolves as `{ ok: false, validationErrors }`\n * rather than rejecting; a network failure (including a 412 conflict from a concurrent edit)\n * rejects normally, surfaced via the returned mutation's `error`.\n */\nexport function useSaveFormTemplate(\n options?: Omit<\n UseMutationOptions<\n SaveFormTemplateDraftResult,\n SynapseClientError,\n SaveFormTemplateDraftParams\n >,\n 'mutationFn'\n >,\n) {\n const queryClient = useQueryClient()\n const { synapseClient, keyFactory } = useSynapseContext()\n const createSchema = useCreateJsonSchema()\n const createTemplate = useCreateFormTemplate()\n const updateTemplate = useUpdateFormTemplate()\n\n return useMutation<\n SaveFormTemplateDraftResult,\n SynapseClientError,\n SaveFormTemplateDraftParams\n >({\n ...options,\n mutationFn: async params => {\n const formTemplateSteps = toFormTemplateSteps(params.steps)\n\n const validationErrors = validateFormTemplateFields(\n formTemplateSteps,\n params.jsonSchema,\n )\n if (validationErrors.length > 0) {\n return { ok: false, validationErrors }\n }\n\n // The schema registry has no update operation — every save that changed the schema body\n // registers a fresh, immutable version. Re-registering an unchanged body would still create\n // an unnecessary version, so skip it when the draft's schema matches what was last resolved.\n let schema$id = params.initialTemplate?.schema$id ?? ''\n const schemaChanged = !isEqual(\n params.jsonSchema,\n params.initialJsonSchema,\n )\n if (schemaChanged || !schema$id) {\n const templateSchemaName = resolveFormTemplateSchemaName(\n params.name,\n params.initialTemplate?.schema$id,\n )\n // Registered versions are immutable, so the list is read fresh on every save to pick a\n // version that does not exist yet.\n const registeredVersions = await queryClient.fetchQuery({\n ...getJsonSchemaVersionsQuery(\n templateSchemaName.organizationName,\n templateSchemaName.schemaName,\n { synapseClient, keyFactory },\n ),\n staleTime: 0,\n })\n const response = await createSchema.mutateAsync({\n ...params.jsonSchema,\n $id: formatSchema$id(\n templateSchemaName,\n getNextSchemaVersion(registeredVersions),\n ),\n })\n schema$id = response.newVersionInfo!.$id!\n }\n\n const formTemplate: FormTemplate = {\n ...params.initialTemplate,\n name: params.name,\n schema$id,\n steps: formTemplateSteps,\n }\n\n const template = params.initialTemplate?.id\n ? await updateTemplate.mutateAsync({\n templateId: params.initialTemplate.id,\n formTemplate,\n })\n : await createTemplate.mutateAsync(formTemplate)\n\n return { ok: true, template }\n },\n })\n}\n"],"mappings":";;;;;;;;;;AAmDA,SAAgB,EACd,GAQA;CACA,IAAM,IAAc,EAAe,GAC7B,EAAE,kBAAe,kBAAe,EAAkB,GAClD,IAAe,EAAoB,GACnC,IAAiB,EAAsB,GACvC,IAAiB,EAAsB;CAE7C,OAAO,EAIL;EACA,GAAG;EACH,YAAY,OAAM,MAAU;GAC1B,IAAM,IAAoB,EAAoB,EAAO,KAAK,GAEpD,IAAmB,EACvB,GACA,EAAO,UACT;GACA,IAAI,EAAiB,SAAS,GAC5B,OAAO;IAAE,IAAI;IAAO;GAAiB;GAMvC,IAAI,IAAY,EAAO,iBAAiB,aAAa;GAKrD,IAAI,CAJmB,EACrB,EAAO,YACP,EAAO,iBACT,KACqB,CAAC,GAAW;IAC/B,IAAM,IAAqB,EACzB,EAAO,MACP,EAAO,iBAAiB,SAC1B,GAGM,IAAqB,MAAM,EAAY,WAAW;KACtD,GAAG,EACD,EAAmB,kBACnB,EAAmB,YACnB;MAAE;MAAe;KAAW,CAC9B;KACA,WAAW;IACb,CAAC;IAQD,KAAY,MAPW,EAAa,YAAY;KAC9C,GAAG,EAAO;KACV,KAAK,EACH,GACA,EAAqB,CAAkB,CACzC;IACF,CAAC,GACoB,eAAgB;GACvC;GAEA,IAAM,IAA6B;IACjC,GAAG,EAAO;IACV,MAAM,EAAO;IACb;IACA,OAAO;GACT;GASA,OAAO;IAAE,IAAI;IAAM,UAPF,EAAO,iBAAiB,KACrC,MAAM,EAAe,YAAY;KAC/B,YAAY,EAAO,gBAAgB;KACnC;IACF,CAAC,IACD,MAAM,EAAe,YAAY,CAAY;GAErB;EAC9B;CACF,CAAC;AACH"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FormTemplate } from '@sage-bionetworks/synapse-client';
|
|
2
|
+
export type FormTemplatePreviewDialogProps = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
template: FormTemplate;
|
|
6
|
+
};
|
|
7
|
+
/** Previews the form that a FormTemplate version presents to a requester. */
|
|
8
|
+
export declare function FormTemplatePreviewDialog(props: FormTemplatePreviewDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=FormTemplatePreviewDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormTemplatePreviewDialog.d.ts","sourceRoot":"","sources":["../../../src/components/FormTemplateReferenceField/FormTemplatePreviewDialog.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAE/D,MAAM,MAAM,8BAA8B,GAAG;IAC3C,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,EAAE,YAAY,CAAA;CACvB,CAAA;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,8BAA8B,2CA8BtC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useGetRegisteredSchema as e } from "../../synapse-queries/jsonschema/useSchema.js";
|
|
2
|
+
import { FormTemplatePreview as t } from "../FormTemplateEditor/FormTemplatePreview.js";
|
|
3
|
+
import { Alert as n, Button as r, Dialog as i, DialogActions as a, DialogContent as o, DialogTitle as s, Skeleton as c } from "@mui/material";
|
|
4
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/FormTemplateReferenceField/FormTemplatePreviewDialog.tsx
|
|
6
|
+
function d(d) {
|
|
7
|
+
let { open: f, onClose: p, template: m } = d, { data: h, isLoading: g, error: _ } = e(m.schema$id, { enabled: f });
|
|
8
|
+
return /* @__PURE__ */ u(i, {
|
|
9
|
+
open: f,
|
|
10
|
+
onClose: p,
|
|
11
|
+
maxWidth: "md",
|
|
12
|
+
fullWidth: !0,
|
|
13
|
+
children: [
|
|
14
|
+
/* @__PURE__ */ u(s, { children: [
|
|
15
|
+
"Preview: ",
|
|
16
|
+
m.name,
|
|
17
|
+
" (version ",
|
|
18
|
+
m.versionNumber,
|
|
19
|
+
")"
|
|
20
|
+
] }),
|
|
21
|
+
/* @__PURE__ */ u(o, { children: [
|
|
22
|
+
g && /* @__PURE__ */ l(c, {
|
|
23
|
+
variant: "rounded",
|
|
24
|
+
height: 200
|
|
25
|
+
}),
|
|
26
|
+
_ && /* @__PURE__ */ l(n, {
|
|
27
|
+
severity: "error",
|
|
28
|
+
children: _.reason
|
|
29
|
+
}),
|
|
30
|
+
h && /* @__PURE__ */ l(t, {
|
|
31
|
+
template: m,
|
|
32
|
+
jsonSchema: h
|
|
33
|
+
})
|
|
34
|
+
] }),
|
|
35
|
+
/* @__PURE__ */ l(a, { children: /* @__PURE__ */ l(r, {
|
|
36
|
+
onClick: p,
|
|
37
|
+
children: "Close"
|
|
38
|
+
}) })
|
|
39
|
+
]
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { d as FormTemplatePreviewDialog };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=FormTemplatePreviewDialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormTemplatePreviewDialog.js","names":[],"sources":["../../../src/components/FormTemplateReferenceField/FormTemplatePreviewDialog.tsx"],"sourcesContent":["import { FormTemplatePreview } from '@/components/FormTemplateEditor/FormTemplatePreview'\nimport { useGetRegisteredSchema } from '@/synapse-queries/jsonschema/useSchema'\nimport {\n Alert,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Skeleton,\n} from '@mui/material'\nimport { RJSFSchema } from '@rjsf/utils'\nimport { FormTemplate } from '@sage-bionetworks/synapse-client'\n\nexport type FormTemplatePreviewDialogProps = {\n open: boolean\n onClose: () => void\n template: FormTemplate\n}\n\n/** Previews the form that a FormTemplate version presents to a requester. */\nexport function FormTemplatePreviewDialog(\n props: FormTemplatePreviewDialogProps,\n) {\n const { open, onClose, template } = props\n\n const {\n data: schema,\n isLoading,\n error,\n } = useGetRegisteredSchema(template.schema$id, { enabled: open })\n\n return (\n <Dialog open={open} onClose={onClose} maxWidth=\"md\" fullWidth>\n <DialogTitle>\n Preview: {template.name} (version {template.versionNumber})\n </DialogTitle>\n <DialogContent>\n {isLoading && <Skeleton variant=\"rounded\" height={200} />}\n {error && <Alert severity=\"error\">{error.reason}</Alert>}\n {schema && (\n <FormTemplatePreview\n template={template}\n jsonSchema={schema as RJSFSchema}\n />\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>Close</Button>\n </DialogActions>\n </Dialog>\n )\n}\n"],"mappings":";;;;;AAqBA,SAAgB,EACd,GACA;CACA,IAAM,EAAE,SAAM,YAAS,gBAAa,GAE9B,EACJ,MAAM,GACN,cACA,aACE,EAAuB,EAAS,WAAW,EAAE,SAAS,EAAK,CAAC;CAEhE,OACE,kBAAC,GAAD;EAAc;EAAe;EAAS,UAAS;EAAK,WAAA;YAApD;GACE,kBAAC,GAAD,EAAA,UAAA;IAAa;IACD,EAAS;IAAK;IAAW,EAAS;IAAc;GAC/C,EAAA,CAAA;GACb,kBAAC,GAAD,EAAA,UAAA;IACG,KAAa,kBAAC,GAAD;KAAU,SAAQ;KAAU,QAAQ;IAAM,CAAA;IACvD,KAAS,kBAAC,GAAD;KAAO,UAAS;eAAS,EAAM;IAAc,CAAA;IACtD,KACC,kBAAC,GAAD;KACY;KACV,YAAY;IACb,CAAA;GAEU,EAAA,CAAA;GACf,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IAAQ,SAAS;cAAS;GAAa,CAAA,EAC1B,CAAA;EACT;;AAEZ"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FormTemplate, FormTemplateReference } from '@sage-bionetworks/synapse-client';
|
|
2
|
+
import { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError';
|
|
3
|
+
export declare const SELECT_FORM_TEMPLATE_ERROR = "Select a form template and version for this Access Requirement.";
|
|
4
|
+
/**
|
|
5
|
+
* Why a reference cannot be saved on an Access Requirement, or undefined if it can. These mirror
|
|
6
|
+
* the checks the server makes when an Access Requirement is created or updated.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getFormTemplateReferenceError(reference: FormTemplateReference | undefined, loadedVersion: {
|
|
9
|
+
template?: FormTemplate;
|
|
10
|
+
error?: SynapseClientError;
|
|
11
|
+
}): string | undefined;
|
|
12
|
+
export declare function useFormTemplateReferenceError(reference: FormTemplateReference | undefined): string | undefined;
|
|
13
|
+
export type FormTemplateReferenceFieldProps = {
|
|
14
|
+
value: FormTemplateReference | undefined;
|
|
15
|
+
onChange: (value: FormTemplateReference) => void;
|
|
16
|
+
error?: string | null;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Select the version of a FormTemplate that an Access Requirement presents to requesters.
|
|
20
|
+
*/
|
|
21
|
+
export declare function FormTemplateReferenceField(props: FormTemplateReferenceFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
//# sourceMappingURL=FormTemplateReferenceField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormTemplateReferenceField.d.ts","sourceRoot":"","sources":["../../../src/components/FormTemplateReferenceField/FormTemplateReferenceField.tsx"],"names":[],"mappings":"AAqBA,OAAO,EACL,YAAY,EACZ,qBAAqB,EACtB,MAAM,kCAAkC,CAAA;AAEzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAA;AAI7F,eAAO,MAAM,0BAA0B,oEAC4B,CAAA;AAEnE;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,qBAAqB,GAAG,SAAS,EAC5C,aAAa,EAAE;IAAE,QAAQ,CAAC,EAAE,YAAY,CAAC;IAAC,KAAK,CAAC,EAAE,kBAAkB,CAAA;CAAE,GACrE,MAAM,GAAG,SAAS,CAYpB;AAED,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,qBAAqB,GAAG,SAAS,GAC3C,MAAM,GAAG,SAAS,CASpB;AAYD,MAAM,MAAM,+BAA+B,GAAG;IAC5C,KAAK,EAAE,qBAAqB,GAAG,SAAS,CAAA;IAExC,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAA;IAEhD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB,CAAA;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,2CAqKvC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { useGetFormTemplateVersion as e, useGetFormTemplateVersions as t, useSearchFormTemplatesInfinite as n } from "../../synapse-queries/dataaccess/useFormTemplate.js";
|
|
2
|
+
import { StyledFormControl as r } from "../styled/StyledFormControl.js";
|
|
3
|
+
import "../dataaccess/FormTemplateTable.js";
|
|
4
|
+
import { FormTemplatePreviewDialog as i } from "./FormTemplatePreviewDialog.js";
|
|
5
|
+
import { useId as a, useMemo as o, useState as s } from "react";
|
|
6
|
+
import { Alert as c, Autocomplete as l, Box as u, Button as d, FormHelperText as f, InputLabel as p, MenuItem as m, Select as h, Stack as g, TextField as _, Typography as v } from "@mui/material";
|
|
7
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
8
|
+
import { keepPreviousData as x } from "@tanstack/react-query";
|
|
9
|
+
import { useDebouncedState as S } from "@react-hookz/web";
|
|
10
|
+
//#region src/components/FormTemplateReferenceField/FormTemplateReferenceField.tsx
|
|
11
|
+
var C = "Select a form template and version for this Access Requirement.";
|
|
12
|
+
function w(e, t) {
|
|
13
|
+
if (!e) return C;
|
|
14
|
+
let n = `Version ${e.templateVersionNumber} of the form template with the id '${e.templateId}'`;
|
|
15
|
+
if (t.error) return `${n} could not be loaded: ${t.error.reason}`;
|
|
16
|
+
if (t.template?.deprecated) return `${n} is deprecated, so it cannot be referenced by an Access Requirement.`;
|
|
17
|
+
}
|
|
18
|
+
function T(t) {
|
|
19
|
+
let { data: n, error: r } = e(t?.templateId, t?.templateVersionNumber);
|
|
20
|
+
return w(t, {
|
|
21
|
+
template: n,
|
|
22
|
+
error: r ?? void 0
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function E(e, t) {
|
|
26
|
+
let n = [e.versionNumber === t ? "latest" : void 0, e.deprecated ? "deprecated" : void 0].filter(Boolean);
|
|
27
|
+
return n.length > 0 ? `Version ${e.versionNumber} (${n.join(", ")})` : `Version ${e.versionNumber}`;
|
|
28
|
+
}
|
|
29
|
+
function D(C) {
|
|
30
|
+
let { value: w, onChange: T, error: D } = C, O = a(), [k, A] = S("", 500), [j, M] = s(!1), { data: N, isLoading: P } = n(o(() => ({
|
|
31
|
+
name: k || void 0,
|
|
32
|
+
includeDeprecated: !1
|
|
33
|
+
}), [k]), { placeholderData: x }), { data: F } = e(w?.templateId, w?.templateVersionNumber), { versions: I, latestVersion: L } = t(w?.templateId), R = o(() => {
|
|
34
|
+
let e = N?.pages.flatMap((e) => e.results ?? []) ?? [];
|
|
35
|
+
return F && !e.some((e) => e.id === F.id) ? [F, ...e] : e;
|
|
36
|
+
}, [N, F]), z = L?.versionNumber ?? 0, B = w != null && L != null && !L.deprecated && z > w.templateVersionNumber;
|
|
37
|
+
return /* @__PURE__ */ b(g, {
|
|
38
|
+
sx: { gap: 2 },
|
|
39
|
+
children: [
|
|
40
|
+
/* @__PURE__ */ y(l, {
|
|
41
|
+
id: "formTemplate",
|
|
42
|
+
options: R,
|
|
43
|
+
loading: P,
|
|
44
|
+
value: F ?? null,
|
|
45
|
+
filterOptions: (e) => e,
|
|
46
|
+
onInputChange: (e, t, n) => {
|
|
47
|
+
n === "input" && A(t);
|
|
48
|
+
},
|
|
49
|
+
onChange: (e, t) => {
|
|
50
|
+
t?.id != null && t.versionNumber != null && t.id !== w?.templateId && T({
|
|
51
|
+
templateId: t.id,
|
|
52
|
+
templateVersionNumber: t.versionNumber
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
getOptionLabel: (e) => e.name,
|
|
56
|
+
isOptionEqualToValue: (e, t) => e.id === t.id,
|
|
57
|
+
renderInput: (e) => /* @__PURE__ */ y(_, {
|
|
58
|
+
...e,
|
|
59
|
+
label: "Form template",
|
|
60
|
+
error: !!D,
|
|
61
|
+
helperText: "Search for the form template that requesters complete to request access."
|
|
62
|
+
})
|
|
63
|
+
}),
|
|
64
|
+
w && /* @__PURE__ */ b(r, {
|
|
65
|
+
error: !!D,
|
|
66
|
+
children: [/* @__PURE__ */ y(p, {
|
|
67
|
+
id: O,
|
|
68
|
+
children: "Template version"
|
|
69
|
+
}), /* @__PURE__ */ b(h, {
|
|
70
|
+
labelId: O,
|
|
71
|
+
value: w.templateVersionNumber,
|
|
72
|
+
onChange: (e) => T({
|
|
73
|
+
templateId: w.templateId,
|
|
74
|
+
templateVersionNumber: Number(e.target.value)
|
|
75
|
+
}),
|
|
76
|
+
children: [I.map((e) => /* @__PURE__ */ y(m, {
|
|
77
|
+
value: e.versionNumber,
|
|
78
|
+
disabled: e.deprecated,
|
|
79
|
+
children: E(e, z)
|
|
80
|
+
}, e.versionNumber)), I.every((e) => e.versionNumber !== w.templateVersionNumber) && /* @__PURE__ */ b(m, {
|
|
81
|
+
value: w.templateVersionNumber,
|
|
82
|
+
disabled: !0,
|
|
83
|
+
children: ["Version ", w.templateVersionNumber]
|
|
84
|
+
})]
|
|
85
|
+
})]
|
|
86
|
+
}),
|
|
87
|
+
F?.deprecated && /* @__PURE__ */ b(c, {
|
|
88
|
+
severity: "warning",
|
|
89
|
+
children: [
|
|
90
|
+
"Version ",
|
|
91
|
+
F.versionNumber,
|
|
92
|
+
" of this form template is deprecated. Select another version before saving."
|
|
93
|
+
]
|
|
94
|
+
}),
|
|
95
|
+
B && /* @__PURE__ */ b(c, {
|
|
96
|
+
severity: "info",
|
|
97
|
+
action: /* @__PURE__ */ b(d, {
|
|
98
|
+
color: "inherit",
|
|
99
|
+
size: "small",
|
|
100
|
+
onClick: () => T({
|
|
101
|
+
templateId: w.templateId,
|
|
102
|
+
templateVersionNumber: z
|
|
103
|
+
}),
|
|
104
|
+
children: ["Update to version ", z]
|
|
105
|
+
}),
|
|
106
|
+
children: [
|
|
107
|
+
"A newer version of this form template is available. This Access Requirement stays on version ",
|
|
108
|
+
w.templateVersionNumber,
|
|
109
|
+
" until you update it."
|
|
110
|
+
]
|
|
111
|
+
}),
|
|
112
|
+
F && /* @__PURE__ */ b(u, { children: [
|
|
113
|
+
/* @__PURE__ */ b(v, {
|
|
114
|
+
variant: "body1",
|
|
115
|
+
children: ["Pinned schema: ", /* @__PURE__ */ y("code", { children: F.schema$id })]
|
|
116
|
+
}),
|
|
117
|
+
/* @__PURE__ */ y(d, {
|
|
118
|
+
variant: "outlined",
|
|
119
|
+
sx: { mt: 1 },
|
|
120
|
+
onClick: () => M(!0),
|
|
121
|
+
children: "Preview form"
|
|
122
|
+
}),
|
|
123
|
+
/* @__PURE__ */ y(i, {
|
|
124
|
+
open: j,
|
|
125
|
+
onClose: () => M(!1),
|
|
126
|
+
template: F
|
|
127
|
+
})
|
|
128
|
+
] }),
|
|
129
|
+
D && /* @__PURE__ */ y(f, {
|
|
130
|
+
error: !0,
|
|
131
|
+
children: D
|
|
132
|
+
})
|
|
133
|
+
]
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
export { D as FormTemplateReferenceField, C as SELECT_FORM_TEMPLATE_ERROR, w as getFormTemplateReferenceError, T as useFormTemplateReferenceError };
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=FormTemplateReferenceField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormTemplateReferenceField.js","names":[],"sources":["../../../src/components/FormTemplateReferenceField/FormTemplateReferenceField.tsx"],"sourcesContent":["import { NAME_SEARCH_DEBOUNCE_DELAY_MS } from '@/components/dataaccess/FormTemplateTable'\nimport { StyledFormControl } from '@/components/styled/StyledFormControl'\nimport {\n useGetFormTemplateVersion,\n useGetFormTemplateVersions,\n useSearchFormTemplatesInfinite,\n} from '@/synapse-queries/dataaccess/useFormTemplate'\nimport {\n Alert,\n Autocomplete,\n Box,\n Button,\n FormHelperText,\n InputLabel,\n MenuItem,\n Select,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useDebouncedState } from '@react-hookz/web'\nimport {\n FormTemplate,\n FormTemplateReference,\n} from '@sage-bionetworks/synapse-client'\nimport { keepPreviousData } from '@tanstack/react-query'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport { useId, useMemo, useState } from 'react'\nimport { FormTemplatePreviewDialog } from './FormTemplatePreviewDialog'\n\nexport const SELECT_FORM_TEMPLATE_ERROR =\n 'Select a form template and version for this Access Requirement.'\n\n/**\n * Why a reference cannot be saved on an Access Requirement, or undefined if it can. These mirror\n * the checks the server makes when an Access Requirement is created or updated.\n */\nexport function getFormTemplateReferenceError(\n reference: FormTemplateReference | undefined,\n loadedVersion: { template?: FormTemplate; error?: SynapseClientError },\n): string | undefined {\n if (!reference) {\n return SELECT_FORM_TEMPLATE_ERROR\n }\n const description = `Version ${reference.templateVersionNumber} of the form template with the id '${reference.templateId}'`\n if (loadedVersion.error) {\n return `${description} could not be loaded: ${loadedVersion.error.reason}`\n }\n if (loadedVersion.template?.deprecated) {\n return `${description} is deprecated, so it cannot be referenced by an Access Requirement.`\n }\n return undefined\n}\n\nexport function useFormTemplateReferenceError(\n reference: FormTemplateReference | undefined,\n): string | undefined {\n const { data: template, error } = useGetFormTemplateVersion(\n reference?.templateId,\n reference?.templateVersionNumber,\n )\n return getFormTemplateReferenceError(reference, {\n template,\n error: error ?? undefined,\n })\n}\n\nfunction describeVersion(version: FormTemplate, latestVersionNumber: number) {\n const labels = [\n version.versionNumber === latestVersionNumber ? 'latest' : undefined,\n version.deprecated ? 'deprecated' : undefined,\n ].filter(Boolean)\n return labels.length > 0\n ? `Version ${version.versionNumber} (${labels.join(', ')})`\n : `Version ${version.versionNumber}`\n}\n\nexport type FormTemplateReferenceFieldProps = {\n value: FormTemplateReference | undefined\n /* The template version is never changed except through this callback; a newer version is never adopted automatically. */\n onChange: (value: FormTemplateReference) => void\n /* A reason the current value is not valid, shown below the field */\n error?: string | null\n}\n\n/**\n * Select the version of a FormTemplate that an Access Requirement presents to requesters.\n */\nexport function FormTemplateReferenceField(\n props: FormTemplateReferenceFieldProps,\n) {\n const { value, onChange, error } = props\n const versionLabelId = useId()\n\n const [searchText, setSearchText] = useDebouncedState<string>(\n '',\n NAME_SEARCH_DEBOUNCE_DELAY_MS,\n )\n const [isPreviewOpen, setIsPreviewOpen] = useState(false)\n\n const searchRequest = useMemo(\n () => ({ name: searchText || undefined, includeDeprecated: false }),\n [searchText],\n )\n const { data: searchData, isLoading: isSearching } =\n useSearchFormTemplatesInfinite(searchRequest, {\n placeholderData: keepPreviousData,\n })\n\n const { data: selectedVersion } = useGetFormTemplateVersion(\n value?.templateId,\n value?.templateVersionNumber,\n )\n const { versions, latestVersion } = useGetFormTemplateVersions(\n value?.templateId,\n )\n\n const templateOptions = useMemo(() => {\n const searchResults =\n searchData?.pages.flatMap(page => page.results ?? []) ?? []\n return selectedVersion &&\n !searchResults.some(template => template.id === selectedVersion.id)\n ? [selectedVersion, ...searchResults]\n : searchResults\n }, [searchData, selectedVersion])\n\n const latestVersionNumber = latestVersion?.versionNumber ?? 0\n const canUpdateToLatest =\n value != null &&\n latestVersion != null &&\n !latestVersion.deprecated &&\n latestVersionNumber > value.templateVersionNumber\n\n return (\n <Stack sx={{ gap: 2 }}>\n <Autocomplete<FormTemplate>\n id=\"formTemplate\"\n options={templateOptions}\n loading={isSearching}\n value={selectedVersion ?? null}\n // Names are matched by the server\n filterOptions={options => options}\n onInputChange={(_event, text, reason) => {\n if (reason === 'input') {\n setSearchText(text)\n }\n }}\n onChange={(_event, template) => {\n // Choosing the template that is already selected must not move it to the latest version\n if (\n template?.id != null &&\n template.versionNumber != null &&\n template.id !== value?.templateId\n ) {\n onChange({\n templateId: template.id,\n templateVersionNumber: template.versionNumber,\n })\n }\n }}\n getOptionLabel={template => template.name}\n isOptionEqualToValue={(option, selected) => option.id === selected.id}\n renderInput={params => (\n <TextField\n {...params}\n label=\"Form template\"\n error={Boolean(error)}\n helperText=\"Search for the form template that requesters complete to request access.\"\n />\n )}\n />\n {value && (\n <StyledFormControl error={Boolean(error)}>\n <InputLabel id={versionLabelId}>Template version</InputLabel>\n <Select\n labelId={versionLabelId}\n value={value.templateVersionNumber}\n onChange={event =>\n onChange({\n templateId: value.templateId,\n templateVersionNumber: Number(event.target.value),\n })\n }\n >\n {versions.map(version => (\n <MenuItem\n key={version.versionNumber}\n value={version.versionNumber}\n disabled={version.deprecated}\n >\n {describeVersion(version, latestVersionNumber)}\n </MenuItem>\n ))}\n {versions.every(\n version => version.versionNumber !== value.templateVersionNumber,\n ) && (\n <MenuItem value={value.templateVersionNumber} disabled>\n Version {value.templateVersionNumber}\n </MenuItem>\n )}\n </Select>\n </StyledFormControl>\n )}\n {selectedVersion?.deprecated && (\n <Alert severity=\"warning\">\n Version {selectedVersion.versionNumber} of this form template is\n deprecated. Select another version before saving.\n </Alert>\n )}\n {canUpdateToLatest && (\n <Alert\n severity=\"info\"\n action={\n <Button\n color=\"inherit\"\n size=\"small\"\n onClick={() =>\n onChange({\n templateId: value.templateId,\n templateVersionNumber: latestVersionNumber,\n })\n }\n >\n Update to version {latestVersionNumber}\n </Button>\n }\n >\n A newer version of this form template is available. This Access\n Requirement stays on version {value.templateVersionNumber} until you\n update it.\n </Alert>\n )}\n {selectedVersion && (\n <Box>\n <Typography variant=\"body1\">\n Pinned schema: <code>{selectedVersion.schema$id}</code>\n </Typography>\n <Button\n variant=\"outlined\"\n sx={{ mt: 1 }}\n onClick={() => setIsPreviewOpen(true)}\n >\n Preview form\n </Button>\n <FormTemplatePreviewDialog\n open={isPreviewOpen}\n onClose={() => setIsPreviewOpen(false)}\n template={selectedVersion}\n />\n </Box>\n )}\n {error && <FormHelperText error>{error}</FormHelperText>}\n </Stack>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { DiscussionFilter } from '@sage-bionetworks/synapse-types';
|
|
2
|
-
|
|
3
|
-
forumId: string;
|
|
2
|
+
type ForumTableBaseProps = {
|
|
4
3
|
limit: number;
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/** Called with the clicked thread's ID and the ID of the project it belongs to */
|
|
5
|
+
onClickLink: (threadId: string, projectId?: string) => void;
|
|
6
|
+
noBackgroundColor?: boolean;
|
|
7
|
+
/** Called when the first page loads, with whether there are no threads */
|
|
8
|
+
onIsEmptyChange?: (isEmpty: boolean) => void;
|
|
9
|
+
/** Max height of the table before it scrolls. Use 'none' for full height. @default '250px' */
|
|
10
|
+
maxHeight?: string;
|
|
7
11
|
};
|
|
8
|
-
export
|
|
12
|
+
export type ForumTableProps = ForumTableBaseProps & ({
|
|
13
|
+
forumId: string;
|
|
14
|
+
filter: DiscussionFilter;
|
|
15
|
+
} | {
|
|
16
|
+
entityId: string;
|
|
17
|
+
onViewForumClicked?: () => void;
|
|
18
|
+
});
|
|
19
|
+
export declare function ForumTable(props: ForumTableProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export default ForumTable;
|
|
9
21
|
//# sourceMappingURL=ForumTable.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ForumTable.d.ts","sourceRoot":"","sources":["../../../src/components/Forum/ForumTable.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ForumTable.d.ts","sourceRoot":"","sources":["../../../src/components/Forum/ForumTable.tsx"],"names":[],"mappings":"AASA,OAAO,EACL,gBAAgB,EAGjB,MAAM,iCAAiC,CAAA;AAsBxC,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,kFAAkF;IAClF,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3D,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,0EAA0E;IAC1E,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC5C,8FAA8F;IAC9F,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,mBAAmB,GAC/C,CACI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC7C;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,CACxD,CAAA;AAuEH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,2CA0HhD;AAED,eAAe,UAAU,CAAA"}
|