({ layoutModel, node }: DisplayNodeProps)
| 222 | * The draggable and displayable portion of a leaf node in a layout tree. |
| 223 | */ |
| 224 | const DisplayNode = ({ layoutModel, node }: DisplayNodeProps) => { |
| 225 | const nodeModel = useNodeModel(layoutModel, node); |
| 226 | const tileNodeRef = useRef<HTMLDivElement>(null); |
| 227 | const previewRef = useRef<HTMLDivElement>(null); |
| 228 | const addlProps = useAtomValue(nodeModel.additionalProps); |
| 229 | const devicePixelRatio = useDevicePixelRatio(); |
| 230 | const isEphemeral = useAtomValue(nodeModel.isEphemeral); |
| 231 | const isMagnified = useAtomValue(nodeModel.isMagnified); |
| 232 | |
| 233 | const [{ isDragging }, drag, dragPreview] = useDrag( |
| 234 | () => ({ |
| 235 | type: tileItemType, |
| 236 | canDrag: () => !(isEphemeral || isMagnified), |
| 237 | item: () => node, |
| 238 | collect: (monitor) => ({ |
| 239 | isDragging: monitor.isDragging(), |
| 240 | }), |
| 241 | }), |
| 242 | [node, addlProps, isEphemeral, isMagnified] |
| 243 | ); |
| 244 | |
| 245 | const [previewElementGeneration, setPreviewElementGeneration] = useState(0); |
| 246 | const previewElement = useMemo(() => { |
| 247 | setPreviewElementGeneration(previewElementGeneration + 1); |
| 248 | return ( |
| 249 | <div key="preview" className="tile-preview-container"> |
| 250 | <div |
| 251 | className="tile-preview" |
| 252 | ref={previewRef} |
| 253 | style={{ |
| 254 | width: DragPreviewWidth, |
| 255 | height: DragPreviewHeight, |
| 256 | transform: `scale(${1 / devicePixelRatio})`, |
| 257 | }} |
| 258 | > |
| 259 | {layoutModel.renderPreview?.(nodeModel)} |
| 260 | </div> |
| 261 | </div> |
| 262 | ); |
| 263 | }, [devicePixelRatio, nodeModel]); |
| 264 | |
| 265 | const [previewImage, setPreviewImage] = useState<HTMLImageElement>(null); |
| 266 | const [previewImageGeneration, setPreviewImageGeneration] = useState(0); |
| 267 | const generatePreviewImage = useCallback(() => { |
| 268 | const offsetX = (DragPreviewWidth * devicePixelRatio - DragPreviewWidth) / 2 + 10; |
| 269 | const offsetY = (DragPreviewHeight * devicePixelRatio - DragPreviewHeight) / 2 + 10; |
| 270 | if (previewImage !== null && previewElementGeneration === previewImageGeneration) { |
| 271 | dragPreview(previewImage, { offsetY, offsetX }); |
| 272 | } else if (previewRef.current) { |
| 273 | setPreviewImageGeneration(previewElementGeneration); |
| 274 | toPng(previewRef.current).then((url) => { |
| 275 | const img = new Image(); |
| 276 | img.src = url; |
| 277 | setPreviewImage(img); |
| 278 | dragPreview(img, { offsetY, offsetX }); |
| 279 | }); |
| 280 | } |
| 281 | }, [ |
nothing calls this directly
no test coverage detected