import { Fun, Num, type Optional, Optionals } from '@ephox/katamari'; import { Css, Scroll, type SugarElement, SugarLocation, type SugarPosition, Traverse } from '@ephox/sugar'; import * as OffsetOrigin from '../../alien/OffsetOrigin'; import type { AlloyComponent } from '../../api/component/ComponentApi'; import * as DragCoord from '../../api/data/DragCoord'; import * as Snappables from '../snap/Snappables'; import type { DraggingConfig, DragStartData, SnapsConfig } from './DraggingTypes'; const getCurrentCoord = (target: SugarElement): DragCoord.CoordAdt => Optionals.lift3(Css.getRaw(target, 'left'), Css.getRaw(target, 'top'), Css.getRaw(target, 'position'), (left, top, position) => { const nu = position === 'fixed' ? DragCoord.fixed : DragCoord.offset; return nu( parseInt(left, 10), parseInt(top, 10) ); }).getOrThunk(() => { const location = SugarLocation.absolute(target); return DragCoord.absolute(location.left, location.top); }); const clampCoords = (component: AlloyComponent, coords: DragCoord.CoordAdt, scroll: SugarPosition, origin: SugarPosition, startData: DragStartData): DragCoord.CoordAdt => { const bounds = startData.bounds; const absoluteCoord = DragCoord.asAbsolute(coords, scroll, origin); const newX = Num.clamp(absoluteCoord.left, bounds.x, bounds.x + bounds.width - startData.width); const newY = Num.clamp(absoluteCoord.top, bounds.y, bounds.y + bounds.height - startData.height); const newCoords = DragCoord.absolute(newX, newY); // Translate the absolute coord back into the previous type return coords.fold( // offset () => { const offset = DragCoord.asOffset(newCoords, scroll, origin); return DragCoord.offset(offset.left, offset.top); }, // absolute Fun.constant(newCoords), // fixed () => { const fixed = DragCoord.asFixed(newCoords, scroll, origin); return DragCoord.fixed(fixed.left, fixed.top); } ); }; const calcNewCoord = (component: AlloyComponent, optSnaps: Optional>, currentCoord: DragCoord.CoordAdt, scroll: SugarPosition, origin: SugarPosition, delta: SugarPosition, startData: DragStartData): DragCoord.CoordAdt => { const newCoord = optSnaps.fold(() => { // When not docking, use fixed coordinates. const translated = DragCoord.translate(currentCoord, delta.left, delta.top); const fixedCoord = DragCoord.asFixed(translated, scroll, origin); return DragCoord.fixed(fixedCoord.left, fixedCoord.top); }, (snapInfo) => { const snapping = Snappables.moveOrSnap(component, snapInfo, currentCoord, delta, scroll, origin); snapping.extra.each((extra) => { snapInfo.onSensor(component, extra); }); return snapping.coord; }); // Clamp the coords so that they are within the bounds return clampCoords(component, newCoord, scroll, origin, startData); }; const dragBy = (component: AlloyComponent, dragConfig: DraggingConfig, startData: DragStartData, delta: SugarPosition): void => { const target = dragConfig.getTarget(component.element); if (dragConfig.repositionTarget) { const doc = Traverse.owner(component.element); const scroll = Scroll.get(doc); const origin = OffsetOrigin.getOrigin(target); const currentCoord = getCurrentCoord(target); const newCoord = calcNewCoord(component, dragConfig.snaps, currentCoord, scroll, origin, delta, startData); const styles = DragCoord.toStyles(newCoord, scroll, origin); Css.setOptions(target, styles); } // NOTE: On drag just goes with the original delta. It does not know about snapping. dragConfig.onDrag(component, target, delta); }; export { dragBy };