Skip to content

Commit

Permalink
188286105 Drag from Plugins to Codap (#1532)
Browse files Browse the repository at this point in the history
* Use function for getOverlayDragId.

* Add app wide plugin AttributeDropOverlay.

* Add notify attribute dragStart, dragMove, and dragEnd handlers.

* Standardize fieldRequiredResults.

* Fix notifications broadcast from attributeLocation API requests.

---------

Co-authored-by: Kirk Swenson <[email protected]>
  • Loading branch information
tealefristoe and kswenson authored Oct 23, 2024
1 parent 2fb7a23 commit 20c21dc
Show file tree
Hide file tree
Showing 25 changed files with 291 additions and 57 deletions.
9 changes: 3 additions & 6 deletions v3/src/components/case-card/case-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { observer } from "mobx-react-lite"
import React, { useCallback, useRef } from "react"
// import { useResizeDetector } from "react-resize-detector"
import { useDataSet } from "../../hooks/use-data-set"
import { getOverlayDragId } from "../../hooks/use-drag-drop"
import { useInstanceIdContext } from "../../hooks/use-instance-id-context"
import { prf } from "../../utilities/profiler"
import { excludeDragOverlayRegEx } from "../case-tile-common/case-tile-types"
// import { DGDataContext } from "../../models/v2/dg-data-context"
import { kIndexColumnKey } from "../case-tile-common/case-tile-types"
import { AttributeDragOverlay } from "../drag-drop/attribute-drag-overlay"
import { CardView } from "./card-view"
import { useCaseCardModel } from "./use-case-card-model"
Expand Down Expand Up @@ -56,10 +57,6 @@ export const CaseCard = observer(function CaseCard({ setNodeRef }: IProps) {
data.selectionChanges // eslint-disable-line no-unused-expressions

return prf.measure("CaseCard.render", () => {
// disable the overlay for the index column
const overlayDragId = active && `${active.id}`.startsWith(instanceId) && !(`${active.id}`.endsWith(kIndexColumnKey))
? `${active.id}` : undefined

// const context = new DGDataContext(data)
const columnWidths: Record<string, number> = {}
cardModel.attributeColumnWidths.forEach((colWidth, id) => {
Expand All @@ -86,7 +83,7 @@ export const CaseCard = observer(function CaseCard({ setNodeRef }: IProps) {
<>
<div ref={mergeRefs} className="case-card react-data-card" data-testid="case-card">
<CardView onNewCollectionDrop={handleNewCollectionDrop}/>
<AttributeDragOverlay activeDragId={overlayDragId} />
<AttributeDragOverlay activeDragId={getOverlayDragId(active, instanceId, excludeDragOverlayRegEx)}/>
</div>
</>
)
Expand Down
9 changes: 3 additions & 6 deletions v3/src/components/case-table/case-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { observer } from "mobx-react-lite"
import React, { useCallback, useEffect, useRef } from "react"
import { CollectionContext, ParentCollectionContext } from "../../hooks/use-collection-context"
import { useDataSetContext } from "../../hooks/use-data-set-context"
import { getOverlayDragId } from "../../hooks/use-drag-drop"
import { useInstanceIdContext } from "../../hooks/use-instance-id-context"
import { registerCanAutoScrollCallback } from "../../lib/dnd-kit/dnd-can-auto-scroll"
import { logMessageWithReplacement } from "../../lib/log-message"
Expand All @@ -13,7 +14,7 @@ import { INotification } from "../../models/history/apply-model-change"
import { mstReaction } from "../../utilities/mst-reaction"
import { prf } from "../../utilities/profiler"
import { t } from "../../utilities/translation/translate"
import { kIndexColumnKey } from "../case-tile-common/case-tile-types"
import { excludeDragOverlayRegEx } from "../case-tile-common/case-tile-types"
import { AttributeHeaderDividerContext } from "../case-tile-common/use-attribute-header-divider-context"
import { AttributeDragOverlay } from "../drag-drop/attribute-drag-overlay"
import { CollectionTable } from "./collection-table"
Expand Down Expand Up @@ -110,10 +111,6 @@ export const CaseTable = observer(function CaseTable({ setNodeRef }: IProps) {
}, [])

return prf.measure("Table.render", () => {
// disable the overlay for the index column
const overlayDragId = active && `${active.id}`.startsWith(instanceId) && !(`${active.id}`.endsWith(kIndexColumnKey))
? `${active.id}` : undefined

if (!tableModel || !data) return null

const collections = data.collections
Expand All @@ -139,7 +136,7 @@ export const CaseTable = observer(function CaseTable({ setNodeRef }: IProps) {
)
})}
</AttributeHeaderDividerContext.Provider>
<AttributeDragOverlay activeDragId={overlayDragId} />
<AttributeDragOverlay activeDragId={getOverlayDragId(active, instanceId, excludeDragOverlayRegEx)} />
<NoCasesMessage />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { V2CaseTable } from "../../data-interactive/data-interactive-component-types"
import { CreateOrShowTileFn, DIComponentHandler } from "../../data-interactive/handlers/component-handler"
import { errorResult } from "../../data-interactive/handlers/di-results"
import { errorResult, fieldRequiredResult } from "../../data-interactive/handlers/di-results"
import { appState } from "../../models/app-state"
import {
getDataSetByNameOrId, getSharedCaseMetadataFromDataset, getSharedDataSetFromDataSetId
Expand All @@ -16,7 +16,7 @@ export const caseTableCardComponentHandler: DIComponentHandler = {
create({ type, values }) {
const { document } = appState
const { dataContext, horizontalScrollOffset } = values as V2CaseTable
const dataContextNotFound = errorResult(t("V3.DI.Error.fieldRequired", { vars: ["Create", type, "dataContext"] }))
const dataContextNotFound = fieldRequiredResult("Create", type, "dataContext")
if (!dataContext) return dataContextNotFound
const dataSet = getDataSetByNameOrId(document, dataContext)
if (!dataSet) return dataContextNotFound
Expand Down
2 changes: 2 additions & 0 deletions v3/src/components/case-tile-common/case-tile-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ export interface IDividerProps {
cellElt: HTMLElement | null
getDividerBounds?: GetDividerBoundsFn
}

export const excludeDragOverlayRegEx = new RegExp(`${kIndexColumnKey}$`)
1 change: 1 addition & 0 deletions v3/src/components/container/container-constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const kContainerClass = "codap-container"
22 changes: 18 additions & 4 deletions v3/src/components/container/container.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { useMergeRefs } from "@chakra-ui/react"
import { useDndContext } from "@dnd-kit/core"
import { clsx } from "clsx"
import React, { useCallback, useRef } from "react"
import { dataInteractiveState } from "../../data-interactive/data-interactive-state"
import { DocumentContainerContext } from "../../hooks/use-document-container-context"
import { useDocumentContent } from "../../hooks/use-document-content"
import { useContainerDroppable, getDragTileId } from "../../hooks/use-drag-drop"
import { useContainerDroppable, getDragTileId, getOverlayDragId } from "../../hooks/use-drag-drop"
import { logMessageWithReplacement, logStringifiedObjectMessage } from "../../lib/log-message"
import { isFreeTileRow } from "../../models/document/free-tile-row"
import { isMosaicTileRow } from "../../models/document/mosaic-tile-row"
import { getSharedModelManager } from "../../models/tiles/tile-environment"
import { urlParams } from "../../utilities/url-params"
import { AttributeDragOverlay } from "../drag-drop/attribute-drag-overlay"
import { PluginAttributeDrag } from "../drag-drop/plugin-attribute-drag"
import { kContainerClass } from "./container-constants"
import { FreeTileRowComponent } from "./free-tile-row"
import { MosaicTileRowComponent } from "./mosaic-tile-row"
import { logMessageWithReplacement, logStringifiedObjectMessage } from "../../lib/log-message"

import "./container.scss"

Expand All @@ -21,6 +26,7 @@ export const Container: React.FC = () => {
const row = documentContent?.getRowByIndex(0)
const getTile = useCallback((tileId: string) => documentContent?.getTile(tileId), [documentContent])
const containerRef = useRef<HTMLDivElement>(null)
const { active } = useDndContext()

const handleCloseTile = useCallback((tileId: string) => {
const tile = getTile(tileId)
Expand All @@ -38,7 +44,7 @@ export const Container: React.FC = () => {
})
}, [documentContent, getTile])

const { setNodeRef } = useContainerDroppable("codap-container", evt => {
const { setNodeRef } = useContainerDroppable(kContainerClass, evt => {
const dragTileId = getDragTileId(evt.active)
if (dragTileId) {
if (isFreeTileRow(row)) {
Expand All @@ -58,14 +64,22 @@ export const Container: React.FC = () => {
})
const mergedContainerRef = useMergeRefs<HTMLDivElement>(containerRef, setNodeRef)

const classes = clsx("codap-container", { "scroll-behavior-auto": isScrollBehaviorAuto })
const classes = clsx(kContainerClass, { "scroll-behavior-auto": isScrollBehaviorAuto })
return (
<DocumentContainerContext.Provider value={containerRef}>
<div className={classes} ref={mergedContainerRef}>
{isMosaicTileRow(row) &&
<MosaicTileRowComponent row={row} getTile={getTile} onCloseTile={handleCloseTile}/>}
{isFreeTileRow(row) &&
<FreeTileRowComponent row={row} getTile={getTile} onCloseTile={handleCloseTile}/>}
<PluginAttributeDrag />
<AttributeDragOverlay
activeDragId={getOverlayDragId(active, "plugin")}
overlayHeight={dataInteractiveState.draggingOverlayHeight}
overlayWidth={dataInteractiveState.draggingOverlayWidth}
xOffset={dataInteractiveState.draggingXOffset}
yOffset={dataInteractiveState.draggingYOffset}
/>
</div>
</DocumentContainerContext.Provider>
)
Expand Down
33 changes: 29 additions & 4 deletions v3/src/components/drag-drop/attribute-drag-overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { DragOverlay, useDndContext } from "@dnd-kit/core"
import React from "react"
import { DragOverlay, Modifier, Modifiers, useDndContext } from "@dnd-kit/core"
import React, { CSSProperties } from "react"
import { getDragAttributeInfo } from "../../hooks/use-drag-drop"

import "./attribute-drag-overlay.scss"

interface IProps {
activeDragId?: string
overlayHeight?: number
overlayWidth?: number
xOffset?: number
yOffset?: number
}

export function AttributeDragOverlay ({ activeDragId }: IProps) {
export function AttributeDragOverlay ({ activeDragId, overlayHeight, overlayWidth, xOffset, yOffset }: IProps) {
const { active } = useDndContext()
const { dataSet, attributeId: dragAttrId } = getDragAttributeInfo(active) || {}
const attr = activeDragId && dragAttrId ? dataSet?.attrFromID(dragAttrId) : undefined
Expand All @@ -18,8 +22,29 @@ export function AttributeDragOverlay ({ activeDragId }: IProps) {
* Otherwise, we don't want to animate it at all.
*/
}

// Drags initiated by plugins can specify the size of the overlay
const style: CSSProperties | undefined = overlayHeight && overlayWidth
? { height: `${overlayHeight}px`, width: `${overlayWidth}px` } : undefined

// Drags initiated by plugins have to be offset based on the location of the plugin
const modifier: Modifier | undefined = (xOffset || yOffset) ? (args => {
const { x, y, scaleX, scaleY } = args.transform
return {
x: x + (xOffset ?? 0),
y: y + (yOffset ?? 0),
scaleX, scaleY
}
}) : undefined
const modifiers: Modifiers | undefined = modifier ? [modifier] : undefined

return (
<DragOverlay className="dnd-kit-drag-overlay" dropAnimation={handleDropAnimation}>
<DragOverlay
className="dnd-kit-drag-overlay"
dropAnimation={handleDropAnimation}
modifiers={modifiers}
style={style}
>
{attr
? <div className="attribute-drag-overlay">
{attr?.name}
Expand Down
1 change: 1 addition & 0 deletions v3/src/components/drag-drop/drag-drop-constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const kPluginAttributeDragId = "plugin-attribute-drag"
7 changes: 7 additions & 0 deletions v3/src/components/drag-drop/plugin-attribute-drag.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#plugin-attribute-drag {
height: 1px;
opacity: 0;
position: absolute;
top: 0px;
width: 1px;
}
26 changes: 26 additions & 0 deletions v3/src/components/drag-drop/plugin-attribute-drag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { observer } from "mobx-react-lite"
import React from "react"
import { dataInteractiveState } from "../../data-interactive/data-interactive-state"
import { useDraggableAttribute } from "../../hooks/use-drag-drop"
import { getDataSetFromId } from "../../models/shared/shared-data-utils"
import { appState } from "../../models/app-state"
import { kPluginAttributeDragId } from "./drag-drop-constants"

import "./plugin-attribute-drag.scss"

export const PluginAttributeDrag = observer(function PluginAttributeDrag() {
const dataSet = getDataSetFromId(appState.document, dataInteractiveState.draggingDatasetId)
const { attributes, listeners, setNodeRef } = useDraggableAttribute({
attributeId: dataInteractiveState.draggingAttributeId,
dataSet,
prefix: "plugin"
})
return (
<div
id={kPluginAttributeDragId}
ref={setNodeRef}
{...attributes}
{...listeners}
/>
)
})
5 changes: 2 additions & 3 deletions v3/src/components/graph/components/graph-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {useMemo} from 'use-memo-one'
import {ITileBaseProps} from '../../tiles/tile-base-props'
import {useDataSet} from '../../../hooks/use-data-set'
import {DataSetContext} from '../../../hooks/use-data-set-context'
import { getOverlayDragId } from '../../../hooks/use-drag-drop'
import {GraphContentModelContext} from '../hooks/use-graph-content-model-context'
import {useGraphController} from "../hooks/use-graph-controller"
import {GraphLayoutContext} from '../hooks/use-graph-layout-context'
Expand Down Expand Up @@ -58,8 +59,6 @@ export const GraphComponent = observer(function GraphComponent({tile}: ITileBase
setNodeRef(graphRef.current ?? null)

const {active} = useDndContext()
const overlayDragId = active && `${active.id}`.startsWith(instanceId)
? `${active.id}` : undefined

if (!graphModel) return null

Expand All @@ -76,7 +75,7 @@ export const GraphComponent = observer(function GraphComponent({tile}: ITileBase
pixiPointsArray={pixiPointsArray}
/>
</AxisProviderContext.Provider>
<AttributeDragOverlay activeDragId={overlayDragId}/>
<AttributeDragOverlay activeDragId={getOverlayDragId(active, instanceId)}/>
</GraphContentModelContext.Provider>
</AxisLayoutContext.Provider>
</GraphLayoutContext.Provider>
Expand Down
5 changes: 2 additions & 3 deletions v3/src/components/map/components/map-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {useDndContext, useDroppable} from '@dnd-kit/core'
import {observer} from "mobx-react-lite"
import React, {useEffect, useRef} from "react"
import {useResizeDetector} from "react-resize-detector"
import { getOverlayDragId } from '../../../hooks/use-drag-drop'
import {InstanceIdContext, useNextInstanceId} from "../../../hooks/use-instance-id-context"
import { selectAllCases } from '../../../models/data/data-set-utils'
import {DataDisplayLayoutContext} from "../../data-display/hooks/use-data-display-layout"
Expand Down Expand Up @@ -41,8 +42,6 @@ export const MapComponent = observer(function MapComponent({tile}: ITileBaseProp
setNodeRef(mapRef.current ?? null)

const {active} = useDndContext()
const overlayDragId = active && `${active.id}`.startsWith(instanceId)
? `${active.id}` : undefined

if (!mapModel) return null

Expand All @@ -51,7 +50,7 @@ export const MapComponent = observer(function MapComponent({tile}: ITileBaseProp
<DataDisplayLayoutContext.Provider value={layout}>
<MapModelContext.Provider value={mapModel}>
<CodapMap mapRef={mapRef}/>
<AttributeDragOverlay activeDragId={overlayDragId}/>
<AttributeDragOverlay activeDragId={getOverlayDragId(active, instanceId)}/>
</MapModelContext.Provider>
</DataDisplayLayoutContext.Provider>
</InstanceIdContext.Provider>
Expand Down
1 change: 1 addition & 0 deletions v3/src/components/web-view/web-view-defs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export const kV2GameType = "game"
export const kV2GuideViewType = "guideView"
export const kV2WebViewType = "webView"
export const kWebViewTileClass = "codap-web-view"
export const kWebViewBodyClass = "codap-web-view-body"
3 changes: 2 additions & 1 deletion v3/src/components/web-view/web-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useRef } from "react"
import { t } from "../../utilities/translation/translate"
import { ITileBaseProps } from "../tiles/tile-base-props"
import { useDataInteractiveController } from "./use-data-interactive-controller"
import { kWebViewBodyClass } from "./web-view-defs"
import { WebViewDropOverlay } from "./web-view-drop-overlay"
import { isWebViewModel } from "./web-view-model"

Expand All @@ -17,7 +18,7 @@ export const WebViewComponent = observer(function WebViewComponent({ tile }: ITi
if (!isWebViewModel(webViewModel)) return null

return (
<div className="codap-web-view-body" data-testid="codap-web-view">
<div className={kWebViewBodyClass} data-testid="codap-web-view">
{!webViewModel.isPlugin && (
<div className="codap-web-view-backdrop">
<div className="codap-web-view-url">{webViewModel.url}</div>
Expand Down
Loading

0 comments on commit 20c21dc

Please sign in to comment.