Skip to content

Commit

Permalink
Progress towards triggering drag programmatically.
Browse files Browse the repository at this point in the history
  • Loading branch information
tealefristoe committed Oct 2, 2024
1 parent e7c2206 commit c87be25
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 58 deletions.
3 changes: 0 additions & 3 deletions v3/src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,11 @@ import { IImportDataSetOptions } from "../models/document/document-content"
import { ISharedDataSet } from "../models/shared/shared-data-set"
import { getSharedModelManager } from "../models/tiles/tile-environment"
import { DocumentContentContext } from "../hooks/use-document-content"
import { getOverlayDragId } from "../hooks/use-drag-drop"
import {useDropHandler} from "../hooks/use-drop-handler"
import { useKeyStates } from "../hooks/use-key-states"
import { registerTileTypes } from "../register-tile-types"
import { importSample, sampleData } from "../sample-data"
import { urlParams } from "../utilities/url-params"
import { AttributeDragOverlay } from "./drag-drop/attribute-drag-overlay"
import { kWebViewTileType } from "./web-view/web-view-defs"
import { isWebViewModel } from "./web-view/web-view-model"
import { logStringifiedObjectMessage } from "../lib/log-message"
Expand Down Expand Up @@ -135,7 +133,6 @@ export const App = observer(function App() {
<MenuBar/>
<ToolShelf document={appState.document}/>
<Container/>
<AttributeDragOverlay activeDragId={getOverlayDragId(active, "plugin")} />
</div>
</DocumentContentContext.Provider>
</CodapDndContext>
Expand Down
10 changes: 8 additions & 2 deletions v3/src/components/container/container.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { useMergeRefs } from "@chakra-ui/react"
import { useDndContext } from "@dnd-kit/core"
import { clsx } from "clsx"
import React, { useCallback, useRef } from "react"
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 { 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 +24,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 Down Expand Up @@ -66,6 +70,8 @@ export const Container: React.FC = () => {
<MosaicTileRowComponent row={row} getTile={getTile} onCloseTile={handleCloseTile}/>}
{isFreeTileRow(row) &&
<FreeTileRowComponent row={row} getTile={getTile} onCloseTile={handleCloseTile}/>}
<PluginAttributeDrag />
<AttributeDragOverlay activeDragId={getOverlayDragId(active, "plugin")} />
</div>
</DocumentContainerContext.Provider>
)
Expand Down
1 change: 1 addition & 0 deletions v3/src/components/drag-drop/attribute-drag-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function AttributeDragOverlay ({ activeDragId }: IProps) {
const { active } = useDndContext()
const { dataSet, attributeId: dragAttrId } = getDragAttributeInfo(active) || {}
const attr = activeDragId && dragAttrId ? dataSet?.attrFromID(dragAttrId) : undefined
console.log(`... attr`, attr)
const handleDropAnimation = (/*params: any*/) => {
/**
* If there has been no drop we would like to animate the overlay back to its original position.
Expand Down
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 {
background-color: red;
height: 50px;
position: absolute;
top: 0px;
width: 50px;
}
24 changes: 24 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,24 @@
import React from "react"
import { useDraggableAttribute } from "../../hooks/use-drag-drop"
import { uiState } from "../../models/ui-state"
import { getDataSetFromId } from "../../models/shared/shared-data-utils"
import { appState } from "../../models/app-state"

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

export function PluginAttributeDrag() {
const dataSet = getDataSetFromId(appState.document, uiState.draggingDatasetId)
const { attributes, listeners, setNodeRef } = useDraggableAttribute({
attributeId: uiState.draggingAttributeId,
dataSet,
prefix: "plugin"
})
return (
<div
id="plugin-attribute-drag"
ref={setNodeRef}
{...attributes}
{...listeners}
/>
)
}
43 changes: 39 additions & 4 deletions v3/src/data-interactive/handlers/attribute-handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { appState } from "../../models/app-state"
import { IAttribute } from "../../models/data/attribute"
import { createAttributesNotification, updateAttributesNotification } from "../../models/data/data-set-notifications"
import { IFreeTileLayout, isFreeTileRow } from "../../models/document/free-tile-row"
import { getSharedCaseMetadataFromDataset } from "../../models/shared/shared-data-utils"
import { uiState } from "../../models/ui-state"
import { t } from "../../utilities/translation/translate"
import { registerDIHandler } from "../data-interactive-handler"
import { convertAttributeToV2, convertAttributeToV2FromResources } from "../data-interactive-type-utils"
Expand Down Expand Up @@ -81,10 +84,42 @@ export const diAttributeHandler: DIHandler = {
if (!request) return fieldRequiredResult("Notify", "attribute", "request")

if (request === "dragStart") {
// Emit an event that will be captured by a custom dnd-kit sensor to start a drag
document.dispatchEvent(new CustomEvent("attributeDragStart", {
detail: { attribute, dataContext }
}))
uiState.setDraggingDatasetId(dataContext.id)
uiState.setDraggingAttributeId(attribute.id)
const pluginAttributeDrag = document.getElementById("plugin-attribute-drag")
if (pluginAttributeDrag) {
// General properties of events
const bubbles = true
const cancelable = true
const isPrimary = true
const pointerId = Date.now()
const pointerType = "mouse"

// Determine position of drag
let height = 10
let width = 10
let x = 0
let y = 0
const { interactiveFrame } = resources
const row = appState.document.content?.firstRow
if (interactiveFrame && row && isFreeTileRow(row)) {
const layout = (row.getTileLayout(interactiveFrame.id) ?? { x: 0, y: 0 }) as IFreeTileLayout
height = layout.height ?? height
width = layout.width ?? width
x = layout.x
y = layout.y
}
const clientX = x + (width / 2)
const clientY = y + (height / 2)

// Dispatch events that will trigger a drag start
pluginAttributeDrag.dispatchEvent(new PointerEvent("pointerdown", {
bubbles, cancelable, clientX, clientY, isPrimary, pointerId, pointerType
}))
document.dispatchEvent(new PointerEvent("pointermove", {
bubbles, cancelable, clientX, clientY, isPrimary, pointerId, pointerType
}))
}
return { success: true }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const diInteractiveFrameHandler: DIHandler = {
get(resources: DIResources) {
const { interactiveFrame } = resources
if (!interactiveFrame) return noIFResult

const dimensions = appState.document.content?.getTileDimensions(interactiveFrame.id)
const webViewContent = isWebViewModel(interactiveFrame.content) ? interactiveFrame.content : undefined
const {
Expand Down
4 changes: 3 additions & 1 deletion v3/src/data-interactive/handlers/item-search-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { registerDIHandler } from "../data-interactive-handler"
import { getV2ItemResult } from "../data-interactive-type-utils"
import { ICase } from "../../models/data/data-set-types"
import { DIHandler, DIItemSearchNotify, DIResources, DIValues } from "../data-interactive-types"
import { couldNotParseQueryResult, dataContextNotFoundResult, fieldRequiredResult, valuesRequiredResult } from "./di-results"
import {
couldNotParseQueryResult, dataContextNotFoundResult, fieldRequiredResult, valuesRequiredResult
} from "./di-results"

export const diItemSearchHandler: DIHandler = {
delete(resources: DIResources) {
Expand Down
28 changes: 22 additions & 6 deletions v3/src/lib/dnd-kit/codap-dnd-context.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {
AutoScrollOptions, DndContext, KeyboardCoordinateGetter, KeyboardSensor,
AutoScrollOptions, DndContext, DragEndEvent, DragStartEvent, KeyboardCoordinateGetter, KeyboardSensor,
MouseSensor, PointerSensor, TraversalOrder, useSensor, useSensors
} from "@dnd-kit/core"
import React, { ReactNode } from "react"
import { containerSnapToGridModifier, restrictDragToArea } from "../../hooks/use-drag-drop"
import { urlParams } from "../../utilities/url-params"
import { canAutoScroll } from "./dnd-can-auto-scroll"
import { dndDetectCollision } from "./dnd-detect-collision"
import { uiState } from "../../models/ui-state"
// import { PluginSensor } from "./plugin-sensor"

interface IProps {
Expand All @@ -27,19 +28,34 @@ export const CodapDndContext = ({ children }: IProps) => {
threshold: { x: 0.05, y: 0.05 }
}

const useMouseSensor = useSensor(MouseSensor)
const handleDragStart = (event: DragStartEvent) => {
console.log(`*** Starting drag`)
}

const handleDragEnd = (event: DragEndEvent) => {
console.log(` ** Ending drag`)
// uiState.setDraggingDatasetId("")
// uiState.setDraggingAttributeId("")
}

// pointer must move three pixels before starting a drag
const pointerSensor = useSensor(PointerSensor, { activationConstraint: { distance: 3 } })
// mouse sensor can be enabled for cypress tests, for instance
const _mouseSensor = useSensor(MouseSensor)
const mouseSensor = urlParams.mouseSensor ? _mouseSensor : null
// const mouseSensor = useSensor(MouseSensor, { activationConstraint: { distance: 3 } })
const sensors = useSensors(
// pointer must move three pixels before starting a drag
useSensor(PointerSensor, { activationConstraint: { distance: 3 }}),
pointerSensor,
useSensor(KeyboardSensor, { coordinateGetter: customCoordinatesGetter }),
// useSensor(PluginSensor),
// mouse sensor can be enabled for cypress tests, for instance
urlParams.mouseSensor !== undefined ? useMouseSensor : null)
mouseSensor)
return (
<DndContext
autoScroll={autoScrollOptions}
collisionDetection={dndDetectCollision}
modifiers={[containerSnapToGridModifier, restrictDragToArea]}
onDragEnd={handleDragEnd}
onDragStart={handleDragStart}
sensors={sensors} >
{children}
</DndContext>
Expand Down
83 changes: 42 additions & 41 deletions v3/src/lib/dnd-kit/plugin-sensor.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,42 @@
// import { PointerEventHandlers, SensorProps } from "@dnd-kit/core"
// import { AbstractPointerSensor, AbstractPointerSensorOptions } from "@dnd-kit/core/dist/sensors"
// import { getOwnerDocument } from "@dnd-kit/utilities"

// const events: PointerEventHandlers = {
// move: { name: "pointermove" },
// end: { name: "pointerup" },
// }

// export interface PluginSensorOptions extends AbstractPointerSensorOptions {}

// export type PluginSensorProps = SensorProps<PluginSensorOptions>;

// export class PluginSensor extends AbstractPointerSensor {
// constructor(props: PluginSensorProps) {
// const { event } = props
// // Pointer events stop firing if the target is unmounted while dragging
// // Therefore we attach listeners to the owner document instead
// const listenerTarget = getOwnerDocument(event.target)

// super(props, events, listenerTarget)
// }

// static activators = [
// {
// eventName: 'onPointerDown' as const,
// handler: (
// event: Event,
// { onActivation }: PluginSensorOptions
// ) => {
// // if (!event.isPrimary || event.button !== 0) {
// // return false
// // }

// onActivation?.({ event })

// return true
// },
// },
// ];
// }
import { AbstractPointerSensor, AbstractPointerSensorOptions, PointerEventHandlers, SensorProps } from "@dnd-kit/core"
// import { SyntheticEventName } from "@dnd-kit/core/dist/types";
import { getOwnerDocument } from "@dnd-kit/utilities"

const events: PointerEventHandlers = {
move: { name: "pointermove" },
end: { name: "pointerup" },
}

export interface PluginSensorOptions extends AbstractPointerSensorOptions {}

export type PluginSensorProps = SensorProps<PluginSensorOptions>;

export class PluginSensor extends AbstractPointerSensor {
constructor(props: PluginSensorProps) {
const { event } = props
// Pointer events stop firing if the target is unmounted while dragging
// Therefore we attach listeners to the owner document instead
const listenerTarget = getOwnerDocument(event.target)

super(props, events, listenerTarget)
}

static activators = [
{
eventName: 'onDragStart' as const,
handler: (
event: Event,
{ onActivation }: PluginSensorOptions
) => {
// if (!event || event.button !== 0) {
// return false
// }

onActivation?.({ event })

console.log(`--- starting drag`)
return true
},
}
];
}
20 changes: 20 additions & 0 deletions v3/src/models/ui-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export class UIState {
@observable
private _interruptionCount = 0

@observable private _draggingDatasetId = ""

@observable private _draggingAttributeId = ""

constructor() {
makeObservable(this)
}
Expand Down Expand Up @@ -71,6 +75,14 @@ export class UIState {
return this._interruptionCount
}

get draggingDatasetId() {
return this._draggingDatasetId
}

get draggingAttributeId() {
return this._draggingAttributeId
}

isFocusedTile(tileId?: string) {
return this.focusTileId === tileId
}
Expand Down Expand Up @@ -123,6 +135,14 @@ export class UIState {
this.attrIdToEdit = attrId
}

@action setDraggingDatasetId(datasetId?: string) {
this._draggingDatasetId = datasetId ?? ""
}

@action setDraggingAttributeId(attributeId?: string) {
this._draggingAttributeId = attributeId ?? ""
}

getRulerStateVisibility(key: RulerStateKey) {
return this.rulerState[key]
}
Expand Down

0 comments on commit c87be25

Please sign in to comment.