addons/isl/src/DraggingOverlay.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import type {DragHandler} from './DragHandle';
b69ab319
b69ab3110import * as stylex from '@stylexjs/stylex';
b69ab3111import {ViewportOverlay} from 'isl-components/ViewportOverlay';
b69ab3112import {getZoomLevel} from 'isl-components/zoom';
b69ab3113import React, {useEffect, useRef} from 'react';
b69ab3114
b69ab3115const styles = stylex.create({
b69ab3116 draggingElement: {
b69ab3117 paddingLeft: 'var(--pad)',
b69ab3118 background: 'var(--background)',
b69ab3119 border: '1px solid var(--tooltip-border)',
b69ab3120 boxShadow: '0px 2px 5px rgba(0, 0, 0, 0.2)',
b69ab3121 },
b69ab3122 hint: {
b69ab3123 display: 'flex',
b69ab3124 justifyContent: 'center',
b69ab3125 marginTop: 'var(--pad)',
b69ab3126 },
b69ab3127});
b69ab3128
b69ab3129type DraggingOverlayProps = React.HTMLProps<HTMLDivElement> & {
b69ab3130 /**
b69ab3131 * Callback ref to update the position of the element.
b69ab3132 *
b69ab3133 * It is compatible with the `onDrag: DragHandler` property of `DragHandler`,
b69ab3134 * or the `clientX`, `clientY` properties of the 'pointermove' event on
b69ab3135 * `document.body`.
b69ab3136 */
b69ab3137 onDragRef: React.MutableRefObject<DragHandler | null>;
b69ab3138
b69ab3139 /** X offset. Default: `- var(--pad)`. */
b69ab3140 dx?: string;
b69ab3141
b69ab3142 /** Y offset. Default: `- 50%`. */
b69ab3143 dy?: string;
b69ab3144
b69ab3145 /** Extra "hint" message. Will be rendered as a tooltip. */
b69ab3146 hint?: string | null;
b69ab3147};
b69ab3148
b69ab3149/**
b69ab3150 * Render children as the "dragging overlay".
b69ab3151 *
b69ab3152 * The callsite needs to update the content (children) and position of
b69ab3153 * the dragging overlay. For performance, the position update requires
b69ab3154 * the callsite to call `props.onDragRef.current` instead of using React
b69ab3155 * props.
b69ab3156 */
b69ab3157export function DraggingOverlay(props: DraggingOverlayProps) {
b69ab3158 const draggingDivRef = useRef<HTMLDivElement | null>(null);
b69ab3159 const {key, children, onDragRef, dx = '- var(--pad)', dy = '- 50%', hint, ...rest} = props;
b69ab3160
b69ab3161 useEffect(() => {
b69ab3162 const zoom = getZoomLevel();
b69ab3163 onDragRef.current = (x, y, isDragging) => {
b69ab3164 const draggingDiv = draggingDivRef.current;
b69ab3165 if (draggingDiv != null) {
b69ab3166 if (isDragging) {
b69ab3167 Object.assign(draggingDiv.style, {
b69ab3168 transform: `translate(calc(${Math.round(x / zoom)}px ${dx}), calc(${Math.round(
b69ab3169 y / zoom,
b69ab3170 )}px ${dy}))`,
b69ab3171 opacity: '1',
b69ab3172 });
b69ab3173 } else {
b69ab3174 draggingDiv.style.opacity = '0';
b69ab3175 }
b69ab3176 }
b69ab3177 };
b69ab3178 });
b69ab3179
b69ab3180 return (
b69ab3181 <ViewportOverlay key={key}>
b69ab3182 <div style={{width: 'fit-content', opacity: 0}} ref={draggingDivRef}>
b69ab3183 <div {...stylex.props(styles.draggingElement)} {...rest}>
b69ab3184 {children}
b69ab3185 </div>
b69ab3186 {hint != null && (
b69ab3187 <div {...stylex.props(styles.hint)}>
b69ab3188 <span className="tooltip" style={{height: 'fit-content'}}>
b69ab3189 {hint}
b69ab3190 </span>
b69ab3191 </div>
b69ab3192 )}
b69ab3193 </div>
b69ab3194 </ViewportOverlay>
b69ab3195 );
b69ab3196}