@wordpress/components
Version:
UI components for WordPress.
8 lines (7 loc) • 13.2 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/focal-point-picker/index.tsx"],
"sourcesContent": ["/**\n * External dependencies\n */\nimport clsx from 'clsx';\n/**\n * WordPress dependencies\n */\nimport { __ } from '@wordpress/i18n';\nimport { useEffect, useRef, useState } from '@wordpress/element';\nimport { __experimentalUseDragging as useDragging, useIsomorphicLayoutEffect } from '@wordpress/compose';\n\n/**\n * Internal dependencies\n */\nimport Controls from './controls';\nimport FocalPoint from './focal-point';\nimport Grid from './grid';\nimport Media from './media';\nimport { Container, MediaWrapper, MediaContainer } from './styles/focal-point-picker-style';\nimport { INITIAL_BOUNDS } from './utils';\nimport { useUpdateEffect } from '../utils/hooks';\nimport { StyledLabel, StyledHelp } from '../base-control/styles/base-control-styles';\nimport { VisuallyHidden } from '../visually-hidden';\nimport { jsx as _jsx, jsxs as _jsxs } from \"react/jsx-runtime\";\nconst GRID_OVERLAY_TIMEOUT = 600;\n\n/**\n * Focal Point Picker is a component which creates a UI for identifying the most important visual point of an image.\n *\n * This component addresses a specific problem: with large background images it is common to see undesirable crops,\n * especially when viewing on smaller viewports such as mobile phones. This component allows the selection of\n * the point with the most important visual information and returns it as a pair of numbers between 0 and 1.\n * This value can be easily converted into the CSS `background-position` attribute, and will ensure that the\n * focal point is never cropped out, regardless of viewport.\n *\n * - Example focal point picker value: `{ x: 0.5, y: 0.1 }`\n * - Corresponding CSS: `background-position: 50% 10%;`\n *\n * ```jsx\n * import { FocalPointPicker } from '@wordpress/components';\n * import { useState } from '@wordpress/element';\n *\n * const Example = () => {\n * \tconst [ focalPoint, setFocalPoint ] = useState( {\n * \t\tx: 0.5,\n * \t\ty: 0.5,\n * \t} );\n *\n * \tconst url = '/path/to/image';\n *\n * \t// Example function to render the CSS styles based on Focal Point Picker value\n * \tconst style = {\n * \t\tbackgroundImage: `url(${ url })`,\n * \t\tbackgroundPosition: `${ focalPoint.x * 100 }% ${ focalPoint.y * 100 }%`,\n * \t};\n *\n * \treturn (\n * \t\t<>\n * \t\t\t<FocalPointPicker\n * \t\t\t\turl={ url }\n * \t\t\t\tvalue={ focalPoint }\n * \t\t\t\tonDragStart={ setFocalPoint }\n * \t\t\t\tonDrag={ setFocalPoint }\n * \t\t\t\tonChange={ setFocalPoint }\n * \t\t\t/>\n * \t\t\t<div style={ style } />\n * \t\t</>\n * \t);\n * };\n * ```\n */\nexport function FocalPointPicker({\n // Prevent passing to internal component.\n __nextHasNoMarginBottom: _,\n autoPlay = true,\n className,\n help,\n hideLabelFromVision,\n label,\n onChange,\n onDrag,\n onDragEnd,\n onDragStart,\n resolvePoint,\n url,\n value: valueProp = {\n x: 0.5,\n y: 0.5\n },\n ...restProps\n}) {\n const [point, setPoint] = useState(valueProp);\n const [showGridOverlay, setShowGridOverlay] = useState(false);\n const {\n startDrag,\n endDrag,\n isDragging\n } = useDragging({\n onDragStart: event => {\n dragAreaRef.current?.focus();\n const value = getValueWithinDragArea(event);\n\n // `value` can technically be undefined if getValueWithinDragArea() is\n // called before dragAreaRef is set, but this shouldn't happen in reality.\n if (!value) {\n return;\n }\n onDragStart?.(value, event);\n setPoint(value);\n },\n onDragMove: event => {\n // Prevents text-selection when dragging.\n event.preventDefault();\n const value = getValueWithinDragArea(event);\n if (!value) {\n return;\n }\n onDrag?.(value, event);\n setPoint(value);\n },\n onDragEnd: () => {\n onDragEnd?.();\n onChange?.(point);\n }\n });\n\n // Uses the internal point while dragging or else the value from props.\n const {\n x,\n y\n } = isDragging ? point : valueProp;\n const dragAreaRef = useRef(null);\n const [bounds, setBounds] = useState(INITIAL_BOUNDS);\n const refUpdateBounds = useRef(() => {\n if (!dragAreaRef.current) {\n return;\n }\n const {\n clientWidth: width,\n clientHeight: height\n } = dragAreaRef.current;\n // Falls back to initial bounds if the ref has no size. Since styles\n // give the drag area dimensions even when the media has not loaded\n // this should only happen in unit tests (jsdom).\n setBounds(width > 0 && height > 0 ? {\n width,\n height\n } : {\n ...INITIAL_BOUNDS\n });\n });\n useEffect(() => {\n const updateBounds = refUpdateBounds.current;\n if (!dragAreaRef.current) {\n return;\n }\n const {\n defaultView\n } = dragAreaRef.current.ownerDocument;\n defaultView?.addEventListener('resize', updateBounds);\n return () => defaultView?.removeEventListener('resize', updateBounds);\n }, []);\n\n // Updates the bounds to cover cases of unspecified media or load failures.\n useIsomorphicLayoutEffect(() => void refUpdateBounds.current(), []);\n\n // TODO: Consider refactoring getValueWithinDragArea() into a pure function.\n // https://github.com/WordPress/gutenberg/pull/43872#discussion_r963455173\n const getValueWithinDragArea = ({\n clientX,\n clientY,\n shiftKey\n }) => {\n if (!dragAreaRef.current) {\n return;\n }\n const {\n top,\n left\n } = dragAreaRef.current.getBoundingClientRect();\n let nextX = (clientX - left) / bounds.width;\n let nextY = (clientY - top) / bounds.height;\n // Enables holding shift to jump values by 10%.\n if (shiftKey) {\n nextX = Math.round(nextX / 0.1) * 0.1;\n nextY = Math.round(nextY / 0.1) * 0.1;\n }\n return getFinalValue({\n x: nextX,\n y: nextY\n });\n };\n const getFinalValue = value => {\n const resolvedValue = resolvePoint?.(value) ?? value;\n resolvedValue.x = Math.max(0, Math.min(resolvedValue.x, 1));\n resolvedValue.y = Math.max(0, Math.min(resolvedValue.y, 1));\n const roundToTwoDecimalPlaces = n => Math.round(n * 1e2) / 1e2;\n return {\n x: roundToTwoDecimalPlaces(resolvedValue.x),\n y: roundToTwoDecimalPlaces(resolvedValue.y)\n };\n };\n const arrowKeyStep = event => {\n const {\n code,\n shiftKey\n } = event;\n if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(code)) {\n return;\n }\n event.preventDefault();\n const value = {\n x,\n y\n };\n const step = shiftKey ? 0.1 : 0.01;\n const delta = code === 'ArrowUp' || code === 'ArrowLeft' ? -1 * step : step;\n const axis = code === 'ArrowUp' || code === 'ArrowDown' ? 'y' : 'x';\n value[axis] = value[axis] + delta;\n onChange?.(getFinalValue(value));\n };\n const focalPointPosition = {\n left: x !== undefined ? x * bounds.width : 0.5 * bounds.width,\n top: y !== undefined ? y * bounds.height : 0.5 * bounds.height\n };\n const classes = clsx('components-focal-point-picker-control', className);\n const Label = hideLabelFromVision ? VisuallyHidden : StyledLabel;\n useUpdateEffect(() => {\n setShowGridOverlay(true);\n const timeout = window.setTimeout(() => {\n setShowGridOverlay(false);\n }, GRID_OVERLAY_TIMEOUT);\n return () => window.clearTimeout(timeout);\n }, [x, y]);\n return /*#__PURE__*/_jsxs(Container, {\n ...restProps,\n as: \"fieldset\",\n className: classes,\n children: [!!label && /*#__PURE__*/_jsx(Label, {\n as: \"legend\",\n children: label\n }), /*#__PURE__*/_jsx(MediaWrapper, {\n className: \"components-focal-point-picker-wrapper\",\n children: /*#__PURE__*/_jsxs(MediaContainer, {\n className: \"components-focal-point-picker\",\n onKeyDown: arrowKeyStep,\n onMouseDown: startDrag,\n onBlur: () => {\n if (isDragging) {\n endDrag();\n }\n },\n ref: dragAreaRef,\n role: \"button\",\n tabIndex: -1,\n children: [/*#__PURE__*/_jsx(Grid, {\n bounds: bounds,\n showOverlay: showGridOverlay\n }), /*#__PURE__*/_jsx(Media, {\n alt: __('Media preview'),\n autoPlay: autoPlay,\n onLoad: refUpdateBounds.current,\n src: url\n }), /*#__PURE__*/_jsx(FocalPoint, {\n ...focalPointPosition,\n isDragging: isDragging\n })]\n })\n }), /*#__PURE__*/_jsx(Controls, {\n hasHelpText: !!help,\n point: {\n x,\n y\n },\n onChange: value => {\n onChange?.(getFinalValue(value));\n }\n }), !!help && /*#__PURE__*/_jsx(StyledHelp, {\n children: help\n })]\n });\n}\nexport default FocalPointPicker;"],
"mappings": "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",
"names": ["useDragging", "clsx", "_jsxs", "_jsx", "Grid", "Media", "FocalPoint", "Controls"]
}