UNPKG

@wordpress/components

Version:
229 lines (228 loc) 6.68 kB
// packages/components/src/focal-point-picker/index.tsx import clsx from "clsx"; import { __ } from "@wordpress/i18n"; import { useEffect, useRef, useState } from "@wordpress/element"; import { __experimentalUseDragging as useDragging, useIsomorphicLayoutEffect } from "@wordpress/compose"; import deprecated from "@wordpress/deprecated"; import Controls from "./controls"; import FocalPoint from "./focal-point"; import Grid from "./grid"; import Media from "./media"; import { Container, MediaWrapper, MediaContainer } from "./styles/focal-point-picker-style"; import { INITIAL_BOUNDS } from "./utils"; import { useUpdateEffect } from "../utils/hooks"; import { StyledLabel, StyledHelp } from "../base-control/styles/base-control-styles"; import { VisuallyHidden } from "../visually-hidden"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; var GRID_OVERLAY_TIMEOUT = 600; function FocalPointPicker({ __nextHasNoMarginBottom, autoPlay = true, className, help, hideLabelFromVision, label, onChange, onDrag, onDragEnd, onDragStart, resolvePoint, url, value: valueProp = { x: 0.5, y: 0.5 }, ...restProps }) { const [point, setPoint] = useState(valueProp); const [showGridOverlay, setShowGridOverlay] = useState(false); if (!__nextHasNoMarginBottom) { deprecated("Bottom margin styles for wp.components.FocalPointPicker", { since: "6.7", version: "7.0", hint: "Set the `__nextHasNoMarginBottom` prop to true to start opting into the new styles, which will become the default in a future version." }); } const { startDrag, endDrag, isDragging } = useDragging({ onDragStart: (event) => { dragAreaRef.current?.focus(); const value = getValueWithinDragArea(event); if (!value) { return; } onDragStart?.(value, event); setPoint(value); }, onDragMove: (event) => { event.preventDefault(); const value = getValueWithinDragArea(event); if (!value) { return; } onDrag?.(value, event); setPoint(value); }, onDragEnd: () => { onDragEnd?.(); onChange?.(point); } }); const { x, y } = isDragging ? point : valueProp; const dragAreaRef = useRef(null); const [bounds, setBounds] = useState(INITIAL_BOUNDS); const refUpdateBounds = useRef(() => { if (!dragAreaRef.current) { return; } const { clientWidth: width, clientHeight: height } = dragAreaRef.current; setBounds(width > 0 && height > 0 ? { width, height } : { ...INITIAL_BOUNDS }); }); useEffect(() => { const updateBounds = refUpdateBounds.current; if (!dragAreaRef.current) { return; } const { defaultView } = dragAreaRef.current.ownerDocument; defaultView?.addEventListener("resize", updateBounds); return () => defaultView?.removeEventListener("resize", updateBounds); }, []); useIsomorphicLayoutEffect(() => void refUpdateBounds.current(), []); const getValueWithinDragArea = ({ clientX, clientY, shiftKey }) => { if (!dragAreaRef.current) { return; } const { top, left } = dragAreaRef.current.getBoundingClientRect(); let nextX = (clientX - left) / bounds.width; let nextY = (clientY - top) / bounds.height; if (shiftKey) { nextX = Math.round(nextX / 0.1) * 0.1; nextY = Math.round(nextY / 0.1) * 0.1; } return getFinalValue({ x: nextX, y: nextY }); }; const getFinalValue = (value) => { var _resolvePoint; const resolvedValue = (_resolvePoint = resolvePoint?.(value)) !== null && _resolvePoint !== void 0 ? _resolvePoint : value; resolvedValue.x = Math.max(0, Math.min(resolvedValue.x, 1)); resolvedValue.y = Math.max(0, Math.min(resolvedValue.y, 1)); const roundToTwoDecimalPlaces = (n) => Math.round(n * 100) / 100; return { x: roundToTwoDecimalPlaces(resolvedValue.x), y: roundToTwoDecimalPlaces(resolvedValue.y) }; }; const arrowKeyStep = (event) => { const { code, shiftKey } = event; if (!["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(code)) { return; } event.preventDefault(); const value = { x, y }; const step = shiftKey ? 0.1 : 0.01; const delta = code === "ArrowUp" || code === "ArrowLeft" ? -1 * step : step; const axis = code === "ArrowUp" || code === "ArrowDown" ? "y" : "x"; value[axis] = value[axis] + delta; onChange?.(getFinalValue(value)); }; const focalPointPosition = { left: x !== void 0 ? x * bounds.width : 0.5 * bounds.width, top: y !== void 0 ? y * bounds.height : 0.5 * bounds.height }; const classes = clsx("components-focal-point-picker-control", className); const Label = hideLabelFromVision ? VisuallyHidden : StyledLabel; useUpdateEffect(() => { setShowGridOverlay(true); const timeout = window.setTimeout(() => { setShowGridOverlay(false); }, GRID_OVERLAY_TIMEOUT); return () => window.clearTimeout(timeout); }, [x, y]); return /* @__PURE__ */ _jsxs(Container, { ...restProps, as: "fieldset", className: classes, children: [!!label && /* @__PURE__ */ _jsx(Label, { as: "legend", children: label }), /* @__PURE__ */ _jsx(MediaWrapper, { className: "components-focal-point-picker-wrapper", children: /* @__PURE__ */ _jsxs(MediaContainer, { className: "components-focal-point-picker", onKeyDown: arrowKeyStep, onMouseDown: startDrag, onBlur: () => { if (isDragging) { endDrag(); } }, ref: dragAreaRef, role: "button", tabIndex: -1, children: [/* @__PURE__ */ _jsx(Grid, { bounds, showOverlay: showGridOverlay }), /* @__PURE__ */ _jsx(Media, { alt: __("Media preview"), autoPlay, onLoad: refUpdateBounds.current, src: url }), /* @__PURE__ */ _jsx(FocalPoint, { ...focalPointPosition, isDragging })] }) }), /* @__PURE__ */ _jsx(Controls, { __nextHasNoMarginBottom, hasHelpText: !!help, point: { x, y }, onChange: (value) => { onChange?.(getFinalValue(value)); } }), !!help && /* @__PURE__ */ _jsx(StyledHelp, { __nextHasNoMarginBottom, children: help })] }); } var focal_point_picker_default = FocalPointPicker; export { FocalPointPicker, focal_point_picker_default as default }; //# sourceMappingURL=index.js.map